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");
}
Example #2
0
    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");
}
Example #5
0
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();
}
Example #6
0
// 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");
}
Example #7
0
// 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");

}