Commit 702374dc authored by Andrew Smith's avatar Andrew Smith Committed by Facebook GitHub Bot

FanoutChannel: Change implementation to use FanoutSender

Summary:
FanoutSender is like FanoutChannel, except that instead of listening to and fanning out values from an input receiver, it allows values to be directly pushed into the sender.

This diff changes FanoutChannel to use FanoutSender, increasing code re-use. FanoutChannel now listens to values from the input receiver, and pushes them into a FanoutSender.

Reviewed By: aary

Differential Revision: D30889891

fbshipit-source-id: 6d2ae416a5a0a895a1b1269d21f6830d45d92184
parent bf1b6d05
......@@ -28,7 +28,7 @@ class IFanoutChannelProcessor;
}
/**
* A fanout channel allows one to fan out updates from a single input receiver
* A fanout channel allows fanning out updates from a single input receiver
* to multiple output receivers.
*
* When a new output receiver is added, an optional function will be run that
......@@ -37,17 +37,16 @@ class IFanoutChannelProcessor;
*
* Example:
*
* // Function that returns a receiver:
* Receiver<int> getInpuReceiverType();
* // Function that returns a receiver:
* Receiver<int> getInputReceiver();
*
* // Function that returns an executor
* folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();
* // Function that returns an executor
* folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();
*
* auto fanoutChannel = createFanoutChannel(geReceiverType(), getExecutor());
* auto receiver1 = fanoutChannel.newReceiver();
* auto receiver2 = fanoutChannel.newReceiver();
* auto receiver3 = fanoutChannel.newReceiver([]{ return {1, 2, 3}; });
* std::move(fanoutChannel).close();
* auto fanoutChannel = createFanoutChannel(getReceiver(), getExecutor());
* auto receiver1 = fanoutChannel.newReceiver();
* auto receiver2 = fanoutChannel.newReceiver();
* auto receiver3 = fanoutChannel.newReceiver([]{ return {1, 2, 3}; });
*/
template <typename ValueType>
class FanoutChannel {
......@@ -66,9 +65,12 @@ class FanoutChannel {
/**
* Returns a new output receiver that will receive all values from the input
* receiver. If a getInitialValues parameter is provided, it will be executed
* to determine the set of initial values that will (only) go to the new input
* receiver.
*
* If a getInitialValues parameter is provided, it will be executed
* to determine the set of initial values that will (only) go to the new input
* receiver. Other functions on this class should not be called from within
* getInitialValues, or a deadlock will occur.
*/
Receiver<ValueType> getNewReceiver(
folly::Function<std::vector<ValueType>()> getInitialValues = {});
......@@ -81,7 +83,7 @@ class FanoutChannel {
/**
* Closes the fanout channel.
*/
void close(std::optional<folly::exception_wrapper> ex = std::nullopt) &&;
void close(folly::exception_wrapper ex = folly::exception_wrapper()) &&;
private:
TProcessor* processor_;
......
......@@ -46,7 +46,7 @@ Receiver<ValueType> FanoutSender<ValueType>::subscribe(
std::vector<ValueType> initialValues) {
clearSendersWithClosedReceivers();
auto [newReceiver, newSender] = Channel<ValueType>::create();
for (auto& initialValue : initialValues) {
for (auto&& initialValue : initialValues) {
newSender.write(std::move(initialValue));
}
if (!anyReceivers()) {
......
......@@ -64,29 +64,41 @@ TEST_F(FanoutChannelFixture, ReceiveValue_FanoutBroadcastsValues) {
auto fanoutChannel =
createFanoutChannel(std::move(inputReceiver), &executor_);
EXPECT_FALSE(fanoutChannel.anyReceivers());
auto [handle1, callback1] = processValues(fanoutChannel.getNewReceiver(
[] { return toVector(100); } /* getInitialValues */));
[]() { return toVector(100); } /* getInitialValues */));
auto [handle2, callback2] = processValues(fanoutChannel.getNewReceiver(
[] { return toVector(200); } /* getInitialValues */));
[]() { return toVector(200); } /* getInitialValues */));
EXPECT_TRUE(fanoutChannel.anyReceivers());
EXPECT_CALL(*callback1, onValue(100));
EXPECT_CALL(*callback2, onValue(200));
executor_.drain();
EXPECT_CALL(*callback1, onValue(1));
EXPECT_CALL(*callback2, onValue(1));
EXPECT_CALL(*callback1, onValue(2));
EXPECT_CALL(*callback2, onValue(2));
EXPECT_CALL(*callback1, onClosed());
EXPECT_CALL(*callback2, onClosed());
sender.write(1);
sender.write(2);
executor_.drain();
EXPECT_TRUE(fanoutChannel.anyReceivers());
auto [handle3, callback3] = processValues(fanoutChannel.getNewReceiver(
[]() { return toVector(300); } /* getInitialValues */));
sender.write(1);
sender.write(2);
EXPECT_CALL(*callback3, onValue(300));
executor_.drain();
sender.write(3);
EXPECT_CALL(*callback1, onValue(3));
EXPECT_CALL(*callback2, onValue(3));
EXPECT_CALL(*callback3, onValue(3));
std::move(sender).close();
EXPECT_CALL(*callback1, onClosed());
EXPECT_CALL(*callback2, onClosed());
EXPECT_CALL(*callback3, onClosed());
executor_.drain();
EXPECT_FALSE(fanoutChannel.anyReceivers());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment