xref: /oneTBB/test/tbb/test_flow_graph.cpp (revision 478de5b1)
151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
17*b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18*b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19*b15aabb3Stbbdev #endif
20*b15aabb3Stbbdev 
2151c0b2f7Stbbdev #include "common/config.h"
2251c0b2f7Stbbdev 
2351c0b2f7Stbbdev #include "tbb/flow_graph.h"
2451c0b2f7Stbbdev 
2551c0b2f7Stbbdev #include "common/test.h"
2651c0b2f7Stbbdev #include "common/utils.h"
2751c0b2f7Stbbdev #include "common/graph_utils.h"
2851c0b2f7Stbbdev #include "common/spin_barrier.h"
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev //! \file test_flow_graph.cpp
3251c0b2f7Stbbdev //! \brief Test for [flow_graph.continue_msg flow_graph.graph_node flow_graph.input_port flow_graph.output_port flow_graph.join_node flow_graph.split_node flow_graph.limiter_node flow_graph.write_once_node flow_graph.overwrite_node flow_graph.make_edge flow_graph.graph flow_graph.buffer_node flow_graph.function_node flow_graph.multifunction_node flow_graph.continue_node flow_graph.input_node] specification
3351c0b2f7Stbbdev 
3451c0b2f7Stbbdev const int T = 4;
3551c0b2f7Stbbdev const int W = 4;
3651c0b2f7Stbbdev 
3751c0b2f7Stbbdev struct decrement_wait : utils::NoAssign {
3851c0b2f7Stbbdev 
3951c0b2f7Stbbdev     tbb::flow::graph * const my_graph;
4051c0b2f7Stbbdev     bool * const my_done_flag;
4151c0b2f7Stbbdev 
decrement_waitdecrement_wait4251c0b2f7Stbbdev     decrement_wait( tbb::flow::graph &h, bool *done_flag ) : my_graph(&h), my_done_flag(done_flag) {}
4351c0b2f7Stbbdev 
operator ()decrement_wait4451c0b2f7Stbbdev     void operator()(int i) const {
4551c0b2f7Stbbdev         utils::Sleep(10 * i);
4651c0b2f7Stbbdev 
4751c0b2f7Stbbdev         my_done_flag[i] = true;
4851c0b2f7Stbbdev         my_graph->release_wait();
4951c0b2f7Stbbdev     }
5051c0b2f7Stbbdev };
5151c0b2f7Stbbdev 
test_wait_count()5251c0b2f7Stbbdev static void test_wait_count() {
5351c0b2f7Stbbdev     tbb::flow::graph h;
5451c0b2f7Stbbdev     for (int i = 0; i < T; ++i ) {
5551c0b2f7Stbbdev         bool done_flag[W];
5651c0b2f7Stbbdev         for (int j = 0; j < W; ++j ) {
5751c0b2f7Stbbdev             for ( int w = 0; w < W; ++w ) done_flag[w] = false;
5851c0b2f7Stbbdev             for ( int w = 0; w < j; ++w ) h.reserve_wait();
5951c0b2f7Stbbdev 
6051c0b2f7Stbbdev             utils::NativeParallelFor( j, decrement_wait(h, done_flag) );
6151c0b2f7Stbbdev             h.wait_for_all();
6251c0b2f7Stbbdev             for ( int w = 0; w < W; ++w ) {
6351c0b2f7Stbbdev                 if ( w < j ) CHECK_MESSAGE( done_flag[w] == true, "" );
6451c0b2f7Stbbdev                 else CHECK_MESSAGE( done_flag[w] == false, "" );
6551c0b2f7Stbbdev             }
6651c0b2f7Stbbdev         }
6751c0b2f7Stbbdev     }
6851c0b2f7Stbbdev }
6951c0b2f7Stbbdev 
7051c0b2f7Stbbdev // Encapsulate object we want to store in vector (because contained type must have
7151c0b2f7Stbbdev // copy constructor and assignment operator
7251c0b2f7Stbbdev class my_int_buffer {
7351c0b2f7Stbbdev     tbb::flow::buffer_node<int> *b;
7451c0b2f7Stbbdev     tbb::flow::graph& my_graph;
7551c0b2f7Stbbdev public:
my_int_buffer(tbb::flow::graph & g)7651c0b2f7Stbbdev     my_int_buffer(tbb::flow::graph &g) : my_graph(g) { b = new tbb::flow::buffer_node<int>(my_graph); }
my_int_buffer(const my_int_buffer & other)7751c0b2f7Stbbdev     my_int_buffer(const my_int_buffer& other) : my_graph(other.my_graph) {
7851c0b2f7Stbbdev         b = new tbb::flow::buffer_node<int>(my_graph);
7951c0b2f7Stbbdev     }
~my_int_buffer()8051c0b2f7Stbbdev     ~my_int_buffer() { delete b; }
operator =(const my_int_buffer &)8151c0b2f7Stbbdev     my_int_buffer& operator=(const my_int_buffer& /*other*/) {
8251c0b2f7Stbbdev         return *this;
8351c0b2f7Stbbdev     }
8451c0b2f7Stbbdev };
8551c0b2f7Stbbdev 
8651c0b2f7Stbbdev // test the graph iterator, delete nodes from graph, test again
test_iterator()8751c0b2f7Stbbdev void test_iterator() {
8851c0b2f7Stbbdev     tbb::flow::graph g;
8951c0b2f7Stbbdev     my_int_buffer a_buffer(g);
9051c0b2f7Stbbdev     my_int_buffer b_buffer(g);
9151c0b2f7Stbbdev     my_int_buffer c_buffer(g);
9251c0b2f7Stbbdev     my_int_buffer *d_buffer = new my_int_buffer(g);
9351c0b2f7Stbbdev     my_int_buffer e_buffer(g);
9451c0b2f7Stbbdev     std::vector< my_int_buffer > my_buffer_vector(10, c_buffer);
9551c0b2f7Stbbdev 
9651c0b2f7Stbbdev     int count = 0;
9751c0b2f7Stbbdev     for (tbb::flow::graph::iterator it = g.begin(); it != g.end(); ++it) {
9851c0b2f7Stbbdev         count++;
9951c0b2f7Stbbdev     }
10051c0b2f7Stbbdev     CHECK_MESSAGE( (count==15), "error in iterator count");
10151c0b2f7Stbbdev 
10251c0b2f7Stbbdev     delete d_buffer;
10351c0b2f7Stbbdev 
10451c0b2f7Stbbdev     count = 0;
10551c0b2f7Stbbdev     for (tbb::flow::graph::iterator it = g.begin(); it != g.end(); ++it) {
10651c0b2f7Stbbdev         count++;
10751c0b2f7Stbbdev     }
10851c0b2f7Stbbdev     CHECK_MESSAGE( (count==14), "error in iterator count");
10951c0b2f7Stbbdev 
11051c0b2f7Stbbdev     my_buffer_vector.clear();
11151c0b2f7Stbbdev 
11251c0b2f7Stbbdev     count = 0;
11351c0b2f7Stbbdev     for (tbb::flow::graph::iterator it = g.begin(); it != g.end(); ++it) {
11451c0b2f7Stbbdev         count++;
11551c0b2f7Stbbdev     }
11651c0b2f7Stbbdev     CHECK_MESSAGE( (count==4), "error in iterator count");
11751c0b2f7Stbbdev }
11851c0b2f7Stbbdev 
11951c0b2f7Stbbdev class AddRemoveBody : utils::NoAssign {
12051c0b2f7Stbbdev     tbb::flow::graph& g;
12151c0b2f7Stbbdev     int nThreads;
12251c0b2f7Stbbdev     utils::SpinBarrier &barrier;
12351c0b2f7Stbbdev public:
AddRemoveBody(int nthr,utils::SpinBarrier & barrier_,tbb::flow::graph & _g)12451c0b2f7Stbbdev     AddRemoveBody(int nthr, utils::SpinBarrier &barrier_, tbb::flow::graph& _g) :
12551c0b2f7Stbbdev         g(_g), nThreads(nthr), barrier(barrier_)
12651c0b2f7Stbbdev     {}
operator ()(const int) const12751c0b2f7Stbbdev     void operator()(const int /*threadID*/) const {
12851c0b2f7Stbbdev         my_int_buffer b(g);
12951c0b2f7Stbbdev         {
13051c0b2f7Stbbdev             std::vector<my_int_buffer> my_buffer_vector(100, b);
13151c0b2f7Stbbdev             barrier.wait();  // wait until all nodes are created
13251c0b2f7Stbbdev             // now test that the proper number of nodes were created
13351c0b2f7Stbbdev             int count = 0;
13451c0b2f7Stbbdev             for (tbb::flow::graph::iterator it = g.begin(); it != g.end(); ++it) {
13551c0b2f7Stbbdev                 count++;
13651c0b2f7Stbbdev             }
13751c0b2f7Stbbdev             CHECK_MESSAGE( (count==101*nThreads), "error in iterator count");
13851c0b2f7Stbbdev             barrier.wait();  // wait until all threads are done counting
13951c0b2f7Stbbdev         } // all nodes but for the initial node on this thread are deleted
14051c0b2f7Stbbdev         barrier.wait(); // wait until all threads have deleted all nodes in their vectors
14151c0b2f7Stbbdev         // now test that all the nodes were deleted except for the initial node
14251c0b2f7Stbbdev         int count = 0;
14351c0b2f7Stbbdev         for (tbb::flow::graph::iterator it = g.begin(); it != g.end(); ++it) {
14451c0b2f7Stbbdev             count++;
14551c0b2f7Stbbdev         }
14651c0b2f7Stbbdev         CHECK_MESSAGE( (count==nThreads), "error in iterator count");
14751c0b2f7Stbbdev         barrier.wait();  // wait until all threads are done counting
14851c0b2f7Stbbdev     } // initial node gets deleted
14951c0b2f7Stbbdev };
15051c0b2f7Stbbdev 
test_parallel(int nThreads)15151c0b2f7Stbbdev void test_parallel(int nThreads) {
15251c0b2f7Stbbdev     tbb::flow::graph g;
15351c0b2f7Stbbdev     utils::SpinBarrier barrier(nThreads);
15451c0b2f7Stbbdev     AddRemoveBody body(nThreads, barrier, g);
15551c0b2f7Stbbdev     NativeParallelFor(nThreads, body);
15651c0b2f7Stbbdev }
15751c0b2f7Stbbdev 
15851c0b2f7Stbbdev /*
15951c0b2f7Stbbdev  * Functors for graph arena spawn tests
16051c0b2f7Stbbdev  */
16151c0b2f7Stbbdev 
check_arena(tbb::task_arena * midway_arena)16251c0b2f7Stbbdev inline void check_arena(tbb::task_arena* midway_arena) {
16351c0b2f7Stbbdev     CHECK_MESSAGE(midway_arena->max_concurrency() == 2, "");
16451c0b2f7Stbbdev     CHECK_MESSAGE(tbb::this_task_arena::max_concurrency() == 1, "");
16551c0b2f7Stbbdev }
16651c0b2f7Stbbdev 
16751c0b2f7Stbbdev struct run_functor {
16851c0b2f7Stbbdev     tbb::task_arena* midway_arena;
16951c0b2f7Stbbdev     int return_value;
run_functorrun_functor17051c0b2f7Stbbdev     run_functor(tbb::task_arena* a) : midway_arena(a), return_value(1) {}
operator ()run_functor17151c0b2f7Stbbdev     int operator()() {
17251c0b2f7Stbbdev         check_arena(midway_arena);
17351c0b2f7Stbbdev         return return_value;
17451c0b2f7Stbbdev     }
17551c0b2f7Stbbdev };
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev template < typename T >
17851c0b2f7Stbbdev struct function_body {
17951c0b2f7Stbbdev     tbb::task_arena* midway_arena;
function_bodyfunction_body18051c0b2f7Stbbdev     function_body(tbb::task_arena* a) : midway_arena(a) {}
operator ()function_body18151c0b2f7Stbbdev     tbb::flow::continue_msg operator()(const T& /*arg*/) {
18251c0b2f7Stbbdev         check_arena(midway_arena);
18351c0b2f7Stbbdev         return tbb::flow::continue_msg();
18451c0b2f7Stbbdev     }
18551c0b2f7Stbbdev };
18651c0b2f7Stbbdev 
18751c0b2f7Stbbdev typedef tbb::flow::multifunction_node< int, std::tuple< int > > mf_node;
18851c0b2f7Stbbdev 
18951c0b2f7Stbbdev struct multifunction_body {
19051c0b2f7Stbbdev     tbb::task_arena* midway_arena;
multifunction_bodymultifunction_body19151c0b2f7Stbbdev     multifunction_body(tbb::task_arena* a) : midway_arena(a) {}
operator ()multifunction_body19251c0b2f7Stbbdev     void operator()(const int& /*arg*/, mf_node::output_ports_type& /*outports*/) {
19351c0b2f7Stbbdev         check_arena(midway_arena);
19451c0b2f7Stbbdev     }
19551c0b2f7Stbbdev };
19651c0b2f7Stbbdev 
19751c0b2f7Stbbdev struct input_body {
19851c0b2f7Stbbdev     tbb::task_arena* midway_arena;
19951c0b2f7Stbbdev     int counter;
input_bodyinput_body20051c0b2f7Stbbdev     input_body(tbb::task_arena* a) : midway_arena(a), counter(0) {}
operator ()input_body20151c0b2f7Stbbdev     int operator()(tbb::flow_control &fc) {
20251c0b2f7Stbbdev         check_arena(midway_arena);
20351c0b2f7Stbbdev         if (counter++ >= 1) {
20451c0b2f7Stbbdev             fc.stop();
20551c0b2f7Stbbdev         }
20651c0b2f7Stbbdev         return int();
20751c0b2f7Stbbdev     }
20851c0b2f7Stbbdev };
20951c0b2f7Stbbdev 
21051c0b2f7Stbbdev struct nodes_test_functor : utils::NoAssign {
21151c0b2f7Stbbdev     tbb::task_arena* midway_arena;
21251c0b2f7Stbbdev     tbb::flow::graph& my_graph;
21351c0b2f7Stbbdev 
nodes_test_functornodes_test_functor21451c0b2f7Stbbdev     nodes_test_functor(tbb::task_arena* a, tbb::flow::graph& g) : midway_arena(a), my_graph(g) {}
operator ()nodes_test_functor21551c0b2f7Stbbdev     void operator()() const {
21651c0b2f7Stbbdev 
21751c0b2f7Stbbdev         // Define test nodes
21851c0b2f7Stbbdev         // Continue, function, source nodes
21951c0b2f7Stbbdev         tbb::flow::continue_node< tbb::flow::continue_msg > c_n(my_graph, function_body<tbb::flow::continue_msg>(midway_arena));
22051c0b2f7Stbbdev         tbb::flow::function_node< int > f_n(my_graph, tbb::flow::unlimited, function_body<int>(midway_arena));
22151c0b2f7Stbbdev         tbb::flow::input_node< int > s_n(my_graph, input_body(midway_arena));
22251c0b2f7Stbbdev 
22351c0b2f7Stbbdev         // Multifunction node
22451c0b2f7Stbbdev         mf_node m_n(my_graph, tbb::flow::unlimited, multifunction_body(midway_arena));
22551c0b2f7Stbbdev 
22651c0b2f7Stbbdev         // Join node
22751c0b2f7Stbbdev         tbb::flow::function_node< std::tuple< int, int > > join_f_n(
22851c0b2f7Stbbdev             my_graph, tbb::flow::unlimited, function_body< std::tuple< int, int > >(midway_arena)
22951c0b2f7Stbbdev         );
23051c0b2f7Stbbdev         tbb::flow::join_node< std::tuple< int, int > > j_n(my_graph);
23151c0b2f7Stbbdev         make_edge(j_n, join_f_n);
23251c0b2f7Stbbdev 
23351c0b2f7Stbbdev         // Split node
23451c0b2f7Stbbdev         tbb::flow::function_node< int > split_f_n1 = f_n;
23551c0b2f7Stbbdev         tbb::flow::function_node< int > split_f_n2 = f_n;
23651c0b2f7Stbbdev         tbb::flow::split_node< std::tuple< int, int > > sp_n(my_graph);
23751c0b2f7Stbbdev         make_edge(tbb::flow::output_port<0>(sp_n), split_f_n1);
23851c0b2f7Stbbdev         make_edge(tbb::flow::output_port<1>(sp_n), split_f_n2);
23951c0b2f7Stbbdev 
24051c0b2f7Stbbdev         // Overwrite node
24151c0b2f7Stbbdev         tbb::flow::function_node< int > ow_f_n = f_n;
24251c0b2f7Stbbdev         tbb::flow::overwrite_node< int > ow_n(my_graph);
24351c0b2f7Stbbdev         make_edge(ow_n, ow_f_n);
24451c0b2f7Stbbdev 
24551c0b2f7Stbbdev         // Write once node
24651c0b2f7Stbbdev         tbb::flow::function_node< int > w_f_n = f_n;
24751c0b2f7Stbbdev         tbb::flow::write_once_node< int > w_n(my_graph);
24851c0b2f7Stbbdev         make_edge(w_n, w_f_n);
24951c0b2f7Stbbdev 
25051c0b2f7Stbbdev         // Buffer node
25151c0b2f7Stbbdev         tbb::flow::function_node< int > buf_f_n = f_n;
25251c0b2f7Stbbdev         tbb::flow::buffer_node< int > buf_n(my_graph);
25351c0b2f7Stbbdev         make_edge(w_n, buf_f_n);
25451c0b2f7Stbbdev 
25551c0b2f7Stbbdev         // Limiter node
25651c0b2f7Stbbdev         tbb::flow::function_node< int > l_f_n = f_n;
25751c0b2f7Stbbdev         tbb::flow::limiter_node< int > l_n(my_graph, 1);
25851c0b2f7Stbbdev         make_edge(l_n, l_f_n);
25951c0b2f7Stbbdev 
26051c0b2f7Stbbdev         // Execute nodes
26151c0b2f7Stbbdev         c_n.try_put( tbb::flow::continue_msg() );
26251c0b2f7Stbbdev         f_n.try_put(1);
26351c0b2f7Stbbdev         m_n.try_put(1);
26451c0b2f7Stbbdev         s_n.activate();
26551c0b2f7Stbbdev 
26651c0b2f7Stbbdev         tbb::flow::input_port<0>(j_n).try_put(1);
26751c0b2f7Stbbdev         tbb::flow::input_port<1>(j_n).try_put(1);
26851c0b2f7Stbbdev 
26951c0b2f7Stbbdev         std::tuple< int, int > sp_tuple(1, 1);
27051c0b2f7Stbbdev         sp_n.try_put(sp_tuple);
27151c0b2f7Stbbdev 
27251c0b2f7Stbbdev         ow_n.try_put(1);
27351c0b2f7Stbbdev         w_n.try_put(1);
27451c0b2f7Stbbdev         buf_n.try_put(1);
27551c0b2f7Stbbdev         l_n.try_put(1);
27651c0b2f7Stbbdev 
27751c0b2f7Stbbdev         my_graph.wait_for_all();
27851c0b2f7Stbbdev     }
27951c0b2f7Stbbdev };
28051c0b2f7Stbbdev 
test_graph_arena()28151c0b2f7Stbbdev void test_graph_arena() {
282*b15aabb3Stbbdev     // There is only one thread for execution (external thread).
28351c0b2f7Stbbdev     // So, if graph's tasks get spawned in different arena
284*b15aabb3Stbbdev     // external thread won't be able to find them in its own arena.
28551c0b2f7Stbbdev     // In this case test should hang.
28651c0b2f7Stbbdev     tbb::task_arena arena(1);
28751c0b2f7Stbbdev 	arena.execute(
28851c0b2f7Stbbdev         [&]() {
28951c0b2f7Stbbdev             tbb::flow::graph g;
29051c0b2f7Stbbdev             tbb::task_arena midway_arena;
29151c0b2f7Stbbdev             midway_arena.initialize(2);
29251c0b2f7Stbbdev             midway_arena.execute(nodes_test_functor(&midway_arena, g));
29351c0b2f7Stbbdev 
29451c0b2f7Stbbdev         }
29551c0b2f7Stbbdev 	);
29651c0b2f7Stbbdev }
29751c0b2f7Stbbdev 
29851c0b2f7Stbbdev //! Test wait counts
29951c0b2f7Stbbdev //! \brief error_guessing
30051c0b2f7Stbbdev TEST_CASE("Test wait_count"){
30151c0b2f7Stbbdev     for(unsigned int p=utils::MinThread; p<=utils::MaxThread; ++p ) {
30251c0b2f7Stbbdev         tbb::task_arena arena(p);
30351c0b2f7Stbbdev         arena.execute(
__anon515993d30202() 30451c0b2f7Stbbdev             [&]() {
30551c0b2f7Stbbdev                 test_wait_count();
30651c0b2f7Stbbdev             }
30751c0b2f7Stbbdev         );
30851c0b2f7Stbbdev 	}
30951c0b2f7Stbbdev }
31051c0b2f7Stbbdev 
31151c0b2f7Stbbdev //! Test graph iterators
31251c0b2f7Stbbdev //! \brief interface
31351c0b2f7Stbbdev TEST_CASE("Test graph::iterator"){
31451c0b2f7Stbbdev     for(unsigned int p=utils::MinThread; p<=utils::MaxThread; ++p ) {
31551c0b2f7Stbbdev         tbb::task_arena arena(p);
31651c0b2f7Stbbdev         arena.execute(
__anon515993d30302() 31751c0b2f7Stbbdev             [&]() {
31851c0b2f7Stbbdev                 test_iterator();
31951c0b2f7Stbbdev             }
32051c0b2f7Stbbdev         );
32151c0b2f7Stbbdev 	}
32251c0b2f7Stbbdev }
32351c0b2f7Stbbdev 
32451c0b2f7Stbbdev //! Test parallel for body
32551c0b2f7Stbbdev //! \brief \ref error_guessing
32651c0b2f7Stbbdev TEST_CASE("Test parallel"){
32751c0b2f7Stbbdev     for(unsigned int p=utils::MinThread; p<=utils::MaxThread; ++p ) {
32851c0b2f7Stbbdev         tbb::task_arena arena(p);
32951c0b2f7Stbbdev         arena.execute(
__anon515993d30402() 33051c0b2f7Stbbdev             [&]() {
33151c0b2f7Stbbdev                 test_parallel(p);
33251c0b2f7Stbbdev             }
33351c0b2f7Stbbdev         );
33451c0b2f7Stbbdev 	}
33551c0b2f7Stbbdev }
33651c0b2f7Stbbdev 
33751c0b2f7Stbbdev //! Test separate arena isn't used
33851c0b2f7Stbbdev //! \brief \ref error_guessing
33951c0b2f7Stbbdev TEST_CASE("Test graph_arena"){
34051c0b2f7Stbbdev     test_graph_arena();
34151c0b2f7Stbbdev }
34251c0b2f7Stbbdev 
34351c0b2f7Stbbdev //! Graph iterator
34451c0b2f7Stbbdev //! \brief \ref error_guessing
34551c0b2f7Stbbdev TEST_CASE("graph iterator") {
34651c0b2f7Stbbdev     using namespace tbb::flow;
34751c0b2f7Stbbdev 
34851c0b2f7Stbbdev     graph g;
34951c0b2f7Stbbdev 
35051c0b2f7Stbbdev     auto past_end = g.end();
35151c0b2f7Stbbdev     ++past_end;
35251c0b2f7Stbbdev 
__anon515993d30502(const continue_msg &)35351c0b2f7Stbbdev     continue_node<int> n(g, [](const continue_msg &){return 1;});
35451c0b2f7Stbbdev 
35551c0b2f7Stbbdev     size_t item_count = 0;
35651c0b2f7Stbbdev 
35751c0b2f7Stbbdev     for(auto it = g.cbegin(); it != g.cend(); it++)
35851c0b2f7Stbbdev         ++item_count;
35951c0b2f7Stbbdev     CHECK_MESSAGE((item_count == 1), "Should find 1 item");
36051c0b2f7Stbbdev 
36151c0b2f7Stbbdev     item_count = 0;
36251c0b2f7Stbbdev     auto jt(g.begin());
36351c0b2f7Stbbdev     for(; jt != g.end(); jt++)
36451c0b2f7Stbbdev         ++item_count;
36551c0b2f7Stbbdev     CHECK_MESSAGE((item_count == 1), "Should find 1 item");
36651c0b2f7Stbbdev 
36751c0b2f7Stbbdev     graph g2;
__anon515993d30602(const continue_msg &)36851c0b2f7Stbbdev     continue_node<int> n2(g, [](const continue_msg &){return 1;});
36951c0b2f7Stbbdev     CHECK_MESSAGE((g.begin() != g2.begin()), "Different graphs should have different iterators");
37051c0b2f7Stbbdev }
371