1.. _broadcast_or_send: 2 3Sending to One or Multiple Successors 4===================================== 5 6 7An important characteristic of the predefined nodes is whether they push 8their output to a single successor or broadcast to all successors. The 9following predefined nodes push messages to a single successor: 10 11 12- buffer_node 13- queue_node 14- priority_queue_node 15- sequencer_node 16 17 18Other nodes push messages to all successors that will accept them. 19 20 21The nodes that push to only a single successor are all buffer nodes. 22Their purpose is to hold messages temporarily, until they are consumed 23downstream. Consider the example below: 24 25 26:: 27 28 29 void use_buffer_and_two_nodes() { 30 graph g; 31 32 33 function_node< int, int, rejecting > f1( g, 1, []( int i ) -> int { 34 spin_for(0.1); 35 cout << "f1 consuming " << i << "\n"; 36 return i; 37 } ); 38 39 40 function_node< int, int, rejecting > f2( g, 1, []( int i ) -> int { 41 spin_for(0.2); 42 cout << "f2 consuming " << i << "\n"; 43 return i; 44 } ); 45 46 47 priority_queue_node< int > q(g); 48 49 50 make_edge( q, f1 ); 51 make_edge( q, f2 ); 52 for ( int i = 10; i > 0; --i ) { 53 q.try_put( i ); 54 } 55 g.wait_for_all(); 56 } 57 58 59First, function_nodes by default queue up the messages they receive at 60their input. To make a priority_queue_node work properly with a 61function_node, the example above constructs its function_nodes with its 62buffer policy set to rejecting. So, f1 and f2 do not internally buffer 63incoming messages, but instead rely on upstream buffering in the 64priority_queue_node. 65 66 67In the above example, each message buffered by the priority_queue_node 68is sent to either f1 or f2, but not both. 69 70 71Let's consider the alternative behavior; that is; what if the 72priority_queue_node broadcasts to all successors. What if some, but not 73all, nodes accept a message? Should the message be buffered until all 74nodes accept it, or be only delivered to the accepting subset? If the 75node continues to buffer the message, should it eventually deliver the 76messages in the same order to all nodes or in the current priority order 77at the time the node accepts the next message? For example, assume a 78priority_queue_node only contains "9" when a successor node, f1, accepts 79"9" but another successor node, f2, rejects it. Later a value "100" 80arrives and f2 is available to accept messages. Should f2 receive "9" 81next or "100", which has a higher priority? In any case, trying to 82ensure that all successors receive each message creates a garbage 83collection problem and complicates reasoning. Therefore, these buffering 84nodes push each message to only one successor. And, you can use this 85characteristic to create useful graph structures such as the one shown 86in the graph above, where each message will be processed in priority 87order, by either f1 or f2. 88 89 90But what if you really do want both f1 and f2 to receive all of the 91values, and in priority order? You can easily create this behavior by 92creating one priority_queue_node for each function_node, and pushing 93each value to both queues through a broadcast_node, as shown below: 94 95 96:: 97 98 99 graph g; 100 101 102 function_node< int, int, rejecting > f1( g, 1, []( int i ) -> int { 103 spin_for(0.1); 104 cout << "f1 consuming " << i << "\n"; 105 return i; 106 } ); 107 108 109 function_node< int, int, rejecting > f2( g, 1, []( int i ) -> int { 110 spin_for(0.2); 111 cout << "f2 consuming " << i << "\n"; 112 return i; 113 } ); 114 115 116 priority_queue_node< int > q1(g); 117 priority_queue_node< int > q2(g); 118 broadcast_node< int > b(g); 119 120 121 make_edge( b, q1 ); 122 make_edge( b, q2 ); 123 make_edge( q1, f1 ); 124 make_edge( q2, f2 ); 125 for ( int i = 10; i > 0; --i ) { 126 b.try_put( i ); 127 } 128 g.wait_for_all(); 129 130 131So, when connecting a node in your graph to multiple successors, be sure 132to understand whether the output will broadcast to all of the 133successors, or just a single successor. 134 135