void TestMultifunctionNode() { typedef tbb::flow::multifunction_node<int, tbb::flow::tuple<int, int>, P> multinode_type; REMARK("Testing multifunction_node"); test_reversal<P,multinode_type> my_test; REMARK(":"); tbb::flow::graph g; multinode_type mf(g, tbb::flow::serial, mf_body<multinode_type>(serial_fn_state0)); tbb::flow::queue_node<int> qin(g); tbb::flow::queue_node<int> qodd_out(g); tbb::flow::queue_node<int> qeven_out(g); tbb::flow::make_edge(qin,mf); tbb::flow::make_edge(tbb::flow::output_port<0>(mf), qeven_out); tbb::flow::make_edge(tbb::flow::output_port<1>(mf), qodd_out); g.wait_for_all(); for( int ii = 0; ii < #if TBB_PREVIEW_FLOW_GRAPH_FEATURES 2 #else 1 #endif ; ++ii) { serial_fn_state0 = 0; if(ii == 0) REMARK(" reset preds"); else REMARK(" 2nd"); qin.try_put(0); // wait for node to be active BACKOFF_WAIT(serial_fn_state0 == 0, "timed out waiting for first put"); qin.try_put(1); BACKOFF_WAIT((!my_test(mf)), "Timed out waiting"); ASSERT(my_test(mf), "fail second put test"); g.my_root_task->cancel_group_execution(); // release node serial_fn_state0 = 2; g.wait_for_all(); ASSERT(my_test(mf), "fail cancel group test"); #if TBB_PREVIEW_FLOW_GRAPH_FEATURES if( ii == 1) { REMARK(" rf_extract"); g.reset(tbb::flow::rf_extract); ASSERT(tbb::flow::output_port<0>(mf).my_successors.empty(), "output_port<0> not reset (rf_extract)"); ASSERT(tbb::flow::output_port<1>(mf).my_successors.empty(), "output_port<1> not reset (rf_extract)"); } else #endif { g.reset(); } ASSERT(mf.my_predecessors.empty(), "edge didn't reset"); ASSERT((ii == 0 && !qin.my_successors.empty()) || (ii == 1 && qin.my_successors.empty()), "edge didn't reset"); } REMARK(" done\n"); }
void operator()( const int& in, typename MF_TYPE::output_ports_type &outports) { if(*_flag == 0) { *_flag = 1; BACKOFF_WAIT(*_flag == 1, "multifunction_node not released"); } if(in & 0x1) tbb::flow::get<1>(outports).try_put(in); else tbb::flow::get<0>(outports).try_put(in); }
// sequencer_node behaves like a queueing node, but requires a different constructor. void TestSequencerNode() { tbb::flow::graph g; tbb::flow::sequencer_node<int> bnode(g, seq_body()); REMARK("Testing sequencer_node:"); tbb::flow::function_node<int> fnode(g, tbb::flow::serial, serial_fn_body<int>(serial_fn_state0)); REMARK("Testing sequencer_node:"); serial_fn_state0 = 0; // reset to waiting state. REMARK(" make_edge"); tbb::flow::make_edge(bnode, fnode); ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after make_edge"); REMARK(" try_put"); bnode.try_put(0); // will forward to the fnode BACKOFF_WAIT( serial_fn_state0 == 0, "timeout waiting for function_node"); // wait for the function_node to fire up ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after forwarding message"); serial_fn_state0 = 0; g.wait_for_all(); REMARK(" remove_edge"); tbb::flow::remove_edge(bnode, fnode); ASSERT(bnode.my_successors.empty(), "buffering node has a successor after remove_edge"); tbb::flow::join_node<tbb::flow::tuple<int,int>,tbb::flow::reserving> jnode(g); tbb::flow::make_edge(bnode, tbb::flow::input_port<0>(jnode)); // will spawn a task g.wait_for_all(); ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after attaching to join"); REMARK(" reverse"); bnode.try_put(3); // the edge should reverse g.wait_for_all(); ASSERT(bnode.my_successors.empty(), "buffering node has a successor after reserving"); REMARK(" reset()"); g.wait_for_all(); g.reset(); // should be in forward direction again ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after reset()"); #if TBB_PREVIEW_FLOW_GRAPH_FEATURES REMARK(" remove_edge"); g.reset(tbb::flow::rf_extract); // should be in forward direction again ASSERT(bnode.my_successors.empty(), "buffering node has a successor after reset(rf_extract)"); ASSERT(fnode.my_predecessors.empty(), "buffering node reversed after reset(rf_extract)"); #endif REMARK(" done\n"); g.wait_for_all(); }
void TestSourceNode() { tbb::flow::graph g; tbb::flow::source_node<int> sn(g, snode_body(4), false); REMARK("Testing source_node:"); tbb::flow::queue_node<int> qin(g); tbb::flow::join_node<tbb::flow::tuple<int,int>, tbb::flow::reserving> jn(g); tbb::flow::queue_node<tbb::flow::tuple<int,int> > qout(g); REMARK(" make_edges"); tbb::flow::make_edge(sn, tbb::flow::input_port<0>(jn)); tbb::flow::make_edge(qin, tbb::flow::input_port<1>(jn)); tbb::flow::make_edge(jn,qout); ASSERT(!sn.my_successors.empty(), "source node has no successor after make_edge"); g.wait_for_all(); g.reset(); ASSERT(!sn.my_successors.empty(), "source node has no successor after reset"); #if TBB_PREVIEW_FLOW_GRAPH_FEATURES g.wait_for_all(); g.reset(tbb::flow::rf_extract); ASSERT(sn.my_successors.empty(), "source node has successor after reset(rf_extract)"); tbb::flow::make_edge(sn, tbb::flow::input_port<0>(jn)); tbb::flow::make_edge(qin, tbb::flow::input_port<1>(jn)); tbb::flow::make_edge(jn,qout); g.wait_for_all(); #endif REMARK(" activate"); sn.activate(); // will forward to the fnode REMARK(" wait1"); BACKOFF_WAIT( !sn.my_successors.empty(), "Timed out waiting for edge to reverse"); ASSERT(sn.my_successors.empty(), "source node has no successor after forwarding message"); g.wait_for_all(); g.reset(); ASSERT(!sn.my_successors.empty(), "source_node has no successors after reset"); ASSERT(tbb::flow::input_port<0>(jn).my_predecessors.empty(), "successor if source_node has pred after reset."); REMARK(" done\n"); }
void TestBufferingNode(const char * name) { tbb::flow::graph g; B bnode(g); tbb::flow::function_node<int,int,tbb::flow::rejecting> fnode(g, tbb::flow::serial, serial_fn_body<int>(serial_fn_state0)); REMARK("Testing %s:", name); for(int icnt = 0; icnt < 2; icnt++) { bool reverse_edge = (icnt & 0x2) != 0; serial_fn_state0 = 0; // reset to waiting state. REMARK(" make_edge"); tbb::flow::make_edge(bnode, fnode); ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after make_edge"); REMARK(" try_put"); bnode.try_put(1); // will forward to the fnode BACKOFF_WAIT(serial_fn_state0 == 0, "Timed out waiting for first put"); if(reverse_edge) { REMARK(" try_put2"); bnode.try_put(2); // will reverse the edge // cannot do a wait_for_all here; the function_node is still executing BACKOFF_WAIT(!bnode.my_successors.empty(), "Timed out waiting after 2nd put"); // at this point the only task running is the one for the function_node. ASSERT(bnode.my_successors.empty(), "successor not removed"); } else { ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after forwarding message"); } serial_fn_state0 = 0; // release the function_node. if(reverse_edge) { // have to do a second release because the function_node will get the 2nd item BACKOFF_WAIT( serial_fn_state0 == 0, "Timed out waiting after 2nd put"); serial_fn_state0 = 0; // release the function_node. } g.wait_for_all(); REMARK(" remove_edge"); tbb::flow::remove_edge(bnode, fnode); ASSERT(bnode.my_successors.empty(), "buffering node has a successor after remove_edge"); } tbb::flow::join_node<tbb::flow::tuple<int,int>,tbb::flow::reserving> jnode(g); tbb::flow::make_edge(bnode, tbb::flow::input_port<0>(jnode)); // will spawn a task g.wait_for_all(); ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after attaching to join"); REMARK(" reverse"); bnode.try_put(1); // the edge should reverse g.wait_for_all(); ASSERT(bnode.my_successors.empty(), "buffering node has a successor after reserving"); REMARK(" reset()"); g.wait_for_all(); g.reset(); // should be in forward direction again ASSERT(!bnode.my_successors.empty(), "buffering node has no successor after reset()"); REMARK(" remove_edge"); g.reset(tbb::flow::rf_clear_edges); ASSERT(bnode.my_successors.empty(), "buffering node has a successor after reset(rf_clear_edges)"); tbb::flow::make_edge(bnode, tbb::flow::input_port<0>(jnode)); // add edge again // reverse edge by adding to buffer. bnode.try_put(1); // the edge should reverse g.wait_for_all(); ASSERT(bnode.my_successors.empty(), "buffering node has a successor after reserving"); REMARK(" remove_edge(reversed)"); g.reset(tbb::flow::rf_clear_edges); ASSERT(bnode.my_successors.empty(), "buffering node has no successor after reset()"); ASSERT(tbb::flow::input_port<0>(jnode).my_predecessors.empty(), "predecessor not reset"); REMARK(" done\n"); g.wait_for_all(); }
// function_node has predecessors and successors // try_get() rejects // successor edges cannot be reversed // predecessors will reverse (only rejecting will reverse) void TestFunctionNode() { tbb::flow::graph g; tbb::flow::queue_node<int> qnode0(g); tbb::flow::function_node<int,int, tbb::flow::rejecting > fnode0(g, tbb::flow::serial, serial_fn_body<int>(serial_fn_state0)); // queueing function node tbb::flow::function_node<int,int> fnode1(g, tbb::flow::serial, serial_fn_body<int>(serial_fn_state0)); tbb::flow::queue_node<int> qnode1(g); tbb::flow::make_edge(fnode0, qnode1); tbb::flow::make_edge(qnode0, fnode0); serial_fn_state0 = 2; // just let it go // see if the darned thing will work.... qnode0.try_put(1); g.wait_for_all(); int ii; ASSERT(qnode1.try_get(ii) && ii == 1, "output not passed"); tbb::flow::remove_edge(qnode0, fnode0); tbb::flow::remove_edge(fnode0, qnode1); tbb::flow::make_edge(fnode1, qnode1); tbb::flow::make_edge(qnode0, fnode1); serial_fn_state0 = 2; // just let it go // see if the darned thing will work.... qnode0.try_put(1); g.wait_for_all(); ASSERT(qnode1.try_get(ii) && ii == 1, "output not passed"); tbb::flow::remove_edge(qnode0, fnode1); tbb::flow::remove_edge(fnode1, qnode1); // rejecting serial_fn_state0 = 0; tbb::flow::make_edge(fnode0, qnode1); tbb::flow::make_edge(qnode0, fnode0); REMARK("Testing rejecting function_node:"); ASSERT(!fnode0.my_queue, "node should have no queue"); ASSERT(!fnode0.my_successors.empty(), "successor edge not added"); qnode0.try_put(1); BACKOFF_WAIT(!serial_fn_state0,"rejecting function_node didn't start"); qnode0.try_put(2); // rejecting node should reject, reverse. BACKOFF_WAIT(fnode0.my_predecessors.empty(), "Missing predecessor ---"); serial_fn_state0 = 2; // release function_node body. g.wait_for_all(); REMARK(" reset"); g.reset(); // should reverse the edge from the input to the function node. ASSERT(!qnode0.my_successors.empty(), "empty successors after reset()"); ASSERT(fnode0.my_predecessors.empty(), "predecessor not reversed"); tbb::flow::remove_edge(qnode0, fnode0); tbb::flow::remove_edge(fnode0, qnode1); REMARK("\n"); // queueing tbb::flow::make_edge(fnode1, qnode1); REMARK("Testing queueing function_node:"); ASSERT(fnode1.my_queue, "node should have no queue"); ASSERT(!fnode1.my_successors.empty(), "successor edge not added"); REMARK(" add_pred"); ASSERT(fnode1.register_predecessor(qnode0), "Cannot register as predecessor"); ASSERT(!fnode1.my_predecessors.empty(), "Missing predecessor"); REMARK(" reset"); g.wait_for_all(); g.reset(); // should reverse the edge from the input to the function node. ASSERT(!qnode0.my_successors.empty(), "empty successors after reset()"); ASSERT(fnode1.my_predecessors.empty(), "predecessor not reversed"); tbb::flow::remove_edge(qnode0, fnode1); tbb::flow::remove_edge(fnode1, qnode1); REMARK("\n"); serial_fn_state0 = 0; // make the function_node wait tbb::flow::make_edge(qnode0, fnode0); REMARK(" start_func"); qnode0.try_put(1); BACKOFF_WAIT(serial_fn_state0 == 0, "Timed out waiting after 1st put"); // now if we put an item to the queues the edges to the function_node will reverse. REMARK(" put_node(2)"); qnode0.try_put(2); // start queue node. // wait for the edges to reverse BACKOFF_WAIT(fnode0.my_predecessors.empty(), "Timed out waiting"); ASSERT(!fnode0.my_predecessors.empty(), "function_node edge not reversed"); g.my_root_task->cancel_group_execution(); // release the function_node serial_fn_state0 = 2; g.wait_for_all(); ASSERT(!fnode0.my_predecessors.empty() && qnode0.my_successors.empty(), "function_node edge not reversed"); g.reset(tbb::flow::rf_clear_edges); ASSERT(fnode0.my_predecessors.empty() && qnode0.my_successors.empty(), "function_node edge not removed"); ASSERT(fnode0.my_successors.empty(), "successor to fnode not removed"); REMARK(" done\n"); }
// continue_node has only predecessor count // they do not have predecessors, only the counts // successor edges cannot be reversed void TestContinueNode() { tbb::flow::graph g; tbb::flow::function_node<int> fnode0(g, tbb::flow::serial, serial_fn_body<int>(serial_fn_state0)); tbb::flow::continue_node<int> cnode(g, 1, serial_continue_body<int>(serial_continue_state0)); tbb::flow::function_node<int> fnode1(g, tbb::flow::serial, serial_fn_body<int>(serial_fn_state1)); tbb::flow::make_edge(fnode0, cnode); tbb::flow::make_edge(cnode, fnode1); REMARK("Testing continue_node:"); for( int icnt = 0; icnt < 2; ++icnt ) { REMARK( " initial%d", icnt); ASSERT(cnode.my_predecessor_count == 2, "predecessor addition didn't increment count"); ASSERT(!cnode.successors().empty(), "successors empty though we added one"); ASSERT(cnode.my_current_count == 0, "state of continue_receiver incorrect"); serial_continue_state0 = 0; serial_fn_state0 = 0; serial_fn_state1 = 0; fnode0.try_put(1); // start the first function node. BACKOFF_WAIT(!serial_fn_state0, "Timed out waiting for function_node to start"); // Now the body of function_node 0 is executing. serial_fn_state0 = 0; // release the node // wait for node to count the message (or for the node body to execute, which would be wrong) BACKOFF_WAIT(serial_continue_state0 == 0 && cnode.my_current_count == 0, "Timed out waiting for continue_state0 to change"); ASSERT(serial_continue_state0 == 0, "Improperly released continue_node"); ASSERT(cnode.my_current_count == 1, "state of continue_receiver incorrect"); if(icnt == 0) { // first time through, let the continue_node fire REMARK(" firing"); fnode0.try_put(1); // second message BACKOFF_WAIT(serial_fn_state0 == 0, "timeout waiting for continue_body to execute"); // Now the body of function_node 0 is executing. serial_fn_state0 = 0; // release the node BACKOFF_WAIT(!serial_continue_state0,"continue_node didn't start"); // now we wait for the continue_node. ASSERT(cnode.my_current_count == 0, " my_current_count not reset before body of continue_node started"); serial_continue_state0 = 0; // release the continue_node BACKOFF_WAIT(!serial_fn_state1,"successor function_node didn't start"); // wait for the successor function_node to enter body serial_fn_state1 = 0; // release successor function_node. g.wait_for_all(); // try a try_get() { int i; ASSERT(!cnode.try_get(i), "try_get not rejected"); } REMARK(" reset"); ASSERT(!cnode.my_successors.empty(), "Empty successors in built graph (before reset)"); ASSERT(cnode.my_predecessor_count == 2, "predecessor_count reset (before reset)"); g.reset(); // should still be the same ASSERT(!cnode.my_successors.empty(), "Empty successors in built graph (after reset)" ); ASSERT(cnode.my_predecessor_count == 2, "predecessor_count reset (after reset)"); } else { // we're going to see if the rf_clear_edges resets things. g.wait_for_all(); REMARK(" reset(rf_clear_edges)"); ASSERT(!cnode.my_successors.empty(), "Empty successors in built graph (before reset)"); ASSERT(cnode.my_predecessor_count == 2, "predecessor_count reset (before reset)"); g.reset(tbb::flow::rf_clear_edges); // should be in forward direction again ASSERT(cnode.my_current_count == 0, "state of continue_receiver incorrect after reset(rf_clear_edges)"); ASSERT(cnode.my_successors.empty(), "buffering node has a successor after reset(rf_clear_edges)"); ASSERT(cnode.my_predecessor_count == cnode.my_initial_predecessor_count, "predecessor count not reset"); } } REMARK(" done\n"); }