xref: /oneTBB/test/tbb/test_sequencer_node.cpp (revision 478de5b1)
151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     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 
1751c0b2f7Stbbdev #include "common/config.h"
1851c0b2f7Stbbdev 
1951c0b2f7Stbbdev #include "tbb/flow_graph.h"
2051c0b2f7Stbbdev 
2151c0b2f7Stbbdev #include "common/test.h"
2251c0b2f7Stbbdev #include "common/utils.h"
2351c0b2f7Stbbdev #include "common/utils_assert.h"
2451c0b2f7Stbbdev #include "common/test_follows_and_precedes_api.h"
25*478de5b1Stbbdev #include "common/concepts_common.h"
2651c0b2f7Stbbdev 
2751c0b2f7Stbbdev #include <cstdio>
2851c0b2f7Stbbdev #include <atomic>
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev //! \file test_sequencer_node.cpp
3251c0b2f7Stbbdev //! \brief Test for [flow_graph.sequencer_node] specification
3351c0b2f7Stbbdev 
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev #define N 1000
3651c0b2f7Stbbdev #define C 10
3751c0b2f7Stbbdev 
3851c0b2f7Stbbdev template< typename T >
3951c0b2f7Stbbdev struct seq_inspector {
4051c0b2f7Stbbdev     size_t operator()(const T &v) const { return size_t(v); }
4151c0b2f7Stbbdev };
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev template< typename T >
4451c0b2f7Stbbdev bool wait_try_get( tbb::flow::graph &g, tbb::flow::sequencer_node<T> &q, T &value ) {
4551c0b2f7Stbbdev     g.wait_for_all();
4651c0b2f7Stbbdev     return q.try_get(value);
4751c0b2f7Stbbdev }
4851c0b2f7Stbbdev 
4951c0b2f7Stbbdev template< typename T >
5051c0b2f7Stbbdev void spin_try_get( tbb::flow::queue_node<T> &q, T &value ) {
5151c0b2f7Stbbdev     while ( q.try_get(value) != true ) ;
5251c0b2f7Stbbdev }
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev template< typename T >
5551c0b2f7Stbbdev struct parallel_puts : utils::NoAssign {
5651c0b2f7Stbbdev 
5751c0b2f7Stbbdev     tbb::flow::sequencer_node<T> &my_q;
5851c0b2f7Stbbdev     int my_num_threads;
5951c0b2f7Stbbdev 
6051c0b2f7Stbbdev     parallel_puts( tbb::flow::sequencer_node<T> &q, int num_threads ) : my_q(q), my_num_threads(num_threads) {}
6151c0b2f7Stbbdev 
6251c0b2f7Stbbdev     void operator()(int tid) const {
6351c0b2f7Stbbdev         for (int j = tid; j < N; j+=my_num_threads) {
6451c0b2f7Stbbdev             bool msg = my_q.try_put( T(j) );
6551c0b2f7Stbbdev             CHECK_MESSAGE( msg == true, "" );
6651c0b2f7Stbbdev         }
6751c0b2f7Stbbdev     }
6851c0b2f7Stbbdev 
6951c0b2f7Stbbdev };
7051c0b2f7Stbbdev 
7151c0b2f7Stbbdev template< typename T >
7251c0b2f7Stbbdev struct touches {
7351c0b2f7Stbbdev 
7451c0b2f7Stbbdev     bool **my_touches;
7551c0b2f7Stbbdev     T *my_last_touch;
7651c0b2f7Stbbdev     int my_num_threads;
7751c0b2f7Stbbdev 
7851c0b2f7Stbbdev     touches( int num_threads ) : my_num_threads(num_threads) {
7951c0b2f7Stbbdev         my_last_touch = new T[my_num_threads];
8051c0b2f7Stbbdev         my_touches = new bool* [my_num_threads];
8151c0b2f7Stbbdev         for ( int p = 0; p < my_num_threads; ++p) {
8251c0b2f7Stbbdev             my_last_touch[p] = T(-1);
8351c0b2f7Stbbdev             my_touches[p] = new bool[N];
8451c0b2f7Stbbdev             for ( int n = 0; n < N; ++n)
8551c0b2f7Stbbdev                 my_touches[p][n] = false;
8651c0b2f7Stbbdev         }
8751c0b2f7Stbbdev     }
8851c0b2f7Stbbdev 
8951c0b2f7Stbbdev     ~touches() {
9051c0b2f7Stbbdev         for ( int p = 0; p < my_num_threads; ++p) {
9151c0b2f7Stbbdev             delete [] my_touches[p];
9251c0b2f7Stbbdev         }
9351c0b2f7Stbbdev         delete [] my_touches;
9451c0b2f7Stbbdev         delete [] my_last_touch;
9551c0b2f7Stbbdev     }
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev     bool check( int tid, T v ) {
9851c0b2f7Stbbdev         if ( my_touches[tid][v] != false ) {
9951c0b2f7Stbbdev             printf("Error: value seen twice by local thread\n");
10051c0b2f7Stbbdev             return false;
10151c0b2f7Stbbdev         }
10251c0b2f7Stbbdev         if ( v <= my_last_touch[tid] ) {
10351c0b2f7Stbbdev             printf("Error: value seen in wrong order by local thread\n");
10451c0b2f7Stbbdev             return false;
10551c0b2f7Stbbdev         }
10651c0b2f7Stbbdev         my_last_touch[tid] = v;
10751c0b2f7Stbbdev         my_touches[tid][v] = true;
10851c0b2f7Stbbdev         return true;
10951c0b2f7Stbbdev     }
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     bool validate_touches() {
11251c0b2f7Stbbdev         bool *all_touches = new bool[N];
11351c0b2f7Stbbdev         for ( int n = 0; n < N; ++n)
11451c0b2f7Stbbdev             all_touches[n] = false;
11551c0b2f7Stbbdev 
11651c0b2f7Stbbdev         for ( int p = 0; p < my_num_threads; ++p) {
11751c0b2f7Stbbdev             for ( int n = 0; n < N; ++n) {
11851c0b2f7Stbbdev                 if ( my_touches[p][n] == true ) {
11951c0b2f7Stbbdev                     CHECK_MESSAGE( ( all_touches[n] == false), "value see by more than one thread\n" );
12051c0b2f7Stbbdev                     all_touches[n] = true;
12151c0b2f7Stbbdev                 }
12251c0b2f7Stbbdev             }
12351c0b2f7Stbbdev         }
12451c0b2f7Stbbdev         for ( int n = 0; n < N; ++n) {
12551c0b2f7Stbbdev             if ( !all_touches[n] )
12651c0b2f7Stbbdev                 printf("No touch at %d, my_num_threads = %d\n", n, my_num_threads);
12751c0b2f7Stbbdev             //CHECK_MESSAGE( ( all_touches[n] == true), "value not seen by any thread\n" );
12851c0b2f7Stbbdev         }
12951c0b2f7Stbbdev         delete [] all_touches;
13051c0b2f7Stbbdev         return true;
13151c0b2f7Stbbdev     }
13251c0b2f7Stbbdev 
13351c0b2f7Stbbdev };
13451c0b2f7Stbbdev 
13551c0b2f7Stbbdev template< typename T >
13651c0b2f7Stbbdev struct parallel_gets : utils::NoAssign {
13751c0b2f7Stbbdev 
13851c0b2f7Stbbdev     tbb::flow::sequencer_node<T> &my_q;
13951c0b2f7Stbbdev     int my_num_threads;
14051c0b2f7Stbbdev     touches<T> &my_touches;
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev     parallel_gets( tbb::flow::sequencer_node<T> &q, int num_threads, touches<T> &t ) : my_q(q), my_num_threads(num_threads), my_touches(t) {}
14351c0b2f7Stbbdev 
14451c0b2f7Stbbdev     void operator()(int tid) const {
14551c0b2f7Stbbdev         for (int j = tid; j < N; j+=my_num_threads) {
14651c0b2f7Stbbdev             T v;
14751c0b2f7Stbbdev             spin_try_get( my_q, v );
14851c0b2f7Stbbdev             my_touches.check( tid, v );
14951c0b2f7Stbbdev         }
15051c0b2f7Stbbdev     }
15151c0b2f7Stbbdev 
15251c0b2f7Stbbdev };
15351c0b2f7Stbbdev 
15451c0b2f7Stbbdev template< typename T >
15551c0b2f7Stbbdev struct parallel_put_get : utils::NoAssign {
15651c0b2f7Stbbdev 
15751c0b2f7Stbbdev     tbb::flow::sequencer_node<T> &my_s1;
15851c0b2f7Stbbdev     tbb::flow::sequencer_node<T> &my_s2;
15951c0b2f7Stbbdev     int my_num_threads;
16051c0b2f7Stbbdev     std::atomic< int > &my_counter;
16151c0b2f7Stbbdev     touches<T> &my_touches;
16251c0b2f7Stbbdev 
16351c0b2f7Stbbdev     parallel_put_get( tbb::flow::sequencer_node<T> &s1, tbb::flow::sequencer_node<T> &s2, int num_threads,
16451c0b2f7Stbbdev                       std::atomic<int> &counter, touches<T> &t ) : my_s1(s1), my_s2(s2), my_num_threads(num_threads), my_counter(counter), my_touches(t) {}
16551c0b2f7Stbbdev 
16651c0b2f7Stbbdev     void operator()(int tid) const {
16751c0b2f7Stbbdev         int i_start = 0;
16851c0b2f7Stbbdev 
16951c0b2f7Stbbdev         while ( (i_start = my_counter.fetch_add(C)) < N ) {
17051c0b2f7Stbbdev             int i_end = ( N < i_start + C ) ? N : i_start + C;
17151c0b2f7Stbbdev             for (int i = i_start; i < i_end; ++i) {
17251c0b2f7Stbbdev                 bool msg = my_s1.try_put( T(i) );
17351c0b2f7Stbbdev                 CHECK_MESSAGE( msg == true, "" );
17451c0b2f7Stbbdev             }
17551c0b2f7Stbbdev 
17651c0b2f7Stbbdev             for (int i = i_start; i < i_end; ++i) {
17751c0b2f7Stbbdev                 T v;
17851c0b2f7Stbbdev                 spin_try_get( my_s2, v );
17951c0b2f7Stbbdev                 my_touches.check( tid, v );
18051c0b2f7Stbbdev             }
18151c0b2f7Stbbdev         }
18251c0b2f7Stbbdev     }
18351c0b2f7Stbbdev 
18451c0b2f7Stbbdev };
18551c0b2f7Stbbdev 
18651c0b2f7Stbbdev //
18751c0b2f7Stbbdev // Tests
18851c0b2f7Stbbdev //
18951c0b2f7Stbbdev // multiple parallel senders, multiple receivers, properly sequenced (relative to receiver) at output
19051c0b2f7Stbbdev // chained sequencers, multiple parallel senders, multiple receivers, properly sequenced (relative to receiver) at output
19151c0b2f7Stbbdev //
19251c0b2f7Stbbdev 
19351c0b2f7Stbbdev template< typename T >
19451c0b2f7Stbbdev int test_parallel(int num_threads) {
19551c0b2f7Stbbdev     tbb::flow::graph g;
19651c0b2f7Stbbdev 
19751c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s(g, seq_inspector<T>());
19851c0b2f7Stbbdev     utils::NativeParallelFor( num_threads, parallel_puts<T>(s, num_threads) );
19951c0b2f7Stbbdev     {
20051c0b2f7Stbbdev         touches<T> t( num_threads );
20151c0b2f7Stbbdev         utils::NativeParallelFor( num_threads, parallel_gets<T>(s, num_threads, t) );
20251c0b2f7Stbbdev         g.wait_for_all();
20351c0b2f7Stbbdev         CHECK_MESSAGE( t.validate_touches(), "" );
20451c0b2f7Stbbdev     }
20551c0b2f7Stbbdev     T bogus_value(-1);
20651c0b2f7Stbbdev     T j = bogus_value;
20751c0b2f7Stbbdev     CHECK_MESSAGE( s.try_get( j ) == false, "" );
20851c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
20951c0b2f7Stbbdev     g.wait_for_all();
21051c0b2f7Stbbdev 
21151c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s1(g, seq_inspector<T>());
21251c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s2(g, seq_inspector<T>());
21351c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s3(g, seq_inspector<T>());
21451c0b2f7Stbbdev     tbb::flow::make_edge( s1, s2 );
21551c0b2f7Stbbdev     tbb::flow::make_edge( s2, s3 );
21651c0b2f7Stbbdev 
21751c0b2f7Stbbdev     {
21851c0b2f7Stbbdev         touches<T> t( num_threads );
21951c0b2f7Stbbdev         std::atomic<int> counter;
22051c0b2f7Stbbdev         counter = 0;
22151c0b2f7Stbbdev         utils::NativeParallelFor( num_threads, parallel_put_get<T>(s1, s3, num_threads, counter, t) );
22251c0b2f7Stbbdev         g.wait_for_all();
22351c0b2f7Stbbdev         t.validate_touches();
22451c0b2f7Stbbdev     }
22551c0b2f7Stbbdev     g.wait_for_all();
22651c0b2f7Stbbdev     CHECK_MESSAGE( s1.try_get( j ) == false, "" );
22751c0b2f7Stbbdev     g.wait_for_all();
22851c0b2f7Stbbdev     CHECK_MESSAGE( s2.try_get( j ) == false, "" );
22951c0b2f7Stbbdev     g.wait_for_all();
23051c0b2f7Stbbdev     CHECK_MESSAGE( s3.try_get( j ) == false, "" );
23151c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
23251c0b2f7Stbbdev 
23351c0b2f7Stbbdev     // test copy constructor
23451c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s_copy(s);
23551c0b2f7Stbbdev     utils::NativeParallelFor( num_threads, parallel_puts<T>(s_copy, num_threads) );
23651c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
23751c0b2f7Stbbdev         j = bogus_value;
23851c0b2f7Stbbdev         spin_try_get( s_copy, j );
23951c0b2f7Stbbdev         CHECK_MESSAGE( i == j, "" );
24051c0b2f7Stbbdev     }
24151c0b2f7Stbbdev     j = bogus_value;
24251c0b2f7Stbbdev     g.wait_for_all();
24351c0b2f7Stbbdev     CHECK_MESSAGE( s_copy.try_get( j ) == false, "" );
24451c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
24551c0b2f7Stbbdev 
24651c0b2f7Stbbdev     return 0;
24751c0b2f7Stbbdev }
24851c0b2f7Stbbdev 
24951c0b2f7Stbbdev 
25051c0b2f7Stbbdev //
25151c0b2f7Stbbdev // Tests
25251c0b2f7Stbbdev //
25351c0b2f7Stbbdev // No predecessors can be registered
25451c0b2f7Stbbdev // Request from empty buffer fails
25551c0b2f7Stbbdev // In-order puts, single sender, single receiver, properly sequenced at output
25651c0b2f7Stbbdev // Reverse-order puts, single sender, single receiver, properly sequenced at output
25751c0b2f7Stbbdev // Chained sequencers (3), in-order and reverse-order tests, properly sequenced at output
25851c0b2f7Stbbdev //
25951c0b2f7Stbbdev 
26051c0b2f7Stbbdev template< typename T >
26151c0b2f7Stbbdev int test_serial() {
26251c0b2f7Stbbdev     tbb::flow::graph g;
26351c0b2f7Stbbdev     T bogus_value(-1);
26451c0b2f7Stbbdev 
26551c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s(g, seq_inspector<T>());
26651c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s2(g, seq_inspector<T>());
26751c0b2f7Stbbdev     T j = bogus_value;
26851c0b2f7Stbbdev 
26951c0b2f7Stbbdev     //
27051c0b2f7Stbbdev     // Rejects attempts to add / remove predecessor
27151c0b2f7Stbbdev     // Rejects request from empty Q
27251c0b2f7Stbbdev     //
27351c0b2f7Stbbdev     CHECK_MESSAGE( register_predecessor( s, s2 ) == false, "" );
27451c0b2f7Stbbdev     CHECK_MESSAGE( remove_predecessor( s, s2 ) == false, "" );
27551c0b2f7Stbbdev     CHECK_MESSAGE( s.try_get( j ) == false, "" );
27651c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
27751c0b2f7Stbbdev 
27851c0b2f7Stbbdev     //
27951c0b2f7Stbbdev     // In-order simple puts and gets
28051c0b2f7Stbbdev     //
28151c0b2f7Stbbdev 
28251c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
28351c0b2f7Stbbdev         bool msg = s.try_put( T(i) );
28451c0b2f7Stbbdev         CHECK_MESSAGE( msg == true, "" );
28551c0b2f7Stbbdev         CHECK_MESSAGE(!s.try_put( T(i) ), "");  // second attempt to put should reject
28651c0b2f7Stbbdev     }
28751c0b2f7Stbbdev 
28851c0b2f7Stbbdev 
28951c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
29051c0b2f7Stbbdev         j = bogus_value;
29151c0b2f7Stbbdev         CHECK_MESSAGE(wait_try_get( g, s, j ) == true, "");
29251c0b2f7Stbbdev         CHECK_MESSAGE( i == j, "" );
29351c0b2f7Stbbdev         CHECK_MESSAGE(!s.try_put( T(i) ),"" );  // after retrieving value, subsequent put should fail
29451c0b2f7Stbbdev     }
29551c0b2f7Stbbdev     j = bogus_value;
29651c0b2f7Stbbdev     g.wait_for_all();
29751c0b2f7Stbbdev     CHECK_MESSAGE( s.try_get( j ) == false, "" );
29851c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
29951c0b2f7Stbbdev 
30051c0b2f7Stbbdev     //
30151c0b2f7Stbbdev     // Reverse-order simple puts and gets
30251c0b2f7Stbbdev     //
30351c0b2f7Stbbdev 
30451c0b2f7Stbbdev     for (int i = N-1; i >= 0; --i) {
30551c0b2f7Stbbdev         bool msg = s2.try_put( T(i) );
30651c0b2f7Stbbdev         CHECK_MESSAGE( msg == true, "" );
30751c0b2f7Stbbdev     }
30851c0b2f7Stbbdev 
30951c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
31051c0b2f7Stbbdev         j = bogus_value;
31151c0b2f7Stbbdev         CHECK_MESSAGE(wait_try_get( g, s2, j ) == true, "");
31251c0b2f7Stbbdev         CHECK_MESSAGE( i == j, "" );
31351c0b2f7Stbbdev     }
31451c0b2f7Stbbdev     j = bogus_value;
31551c0b2f7Stbbdev     g.wait_for_all();
31651c0b2f7Stbbdev     CHECK_MESSAGE( s2.try_get( j ) == false, "" );
31751c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
31851c0b2f7Stbbdev 
31951c0b2f7Stbbdev     //
32051c0b2f7Stbbdev     // Chained in-order simple puts and gets
32151c0b2f7Stbbdev     //
32251c0b2f7Stbbdev 
32351c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s3(g, seq_inspector<T>());
32451c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s4(g, seq_inspector<T>());
32551c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s5(g, seq_inspector<T>());
32651c0b2f7Stbbdev     tbb::flow::make_edge( s3, s4 );
32751c0b2f7Stbbdev     tbb::flow::make_edge( s4, s5 );
32851c0b2f7Stbbdev 
32951c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
33051c0b2f7Stbbdev         bool msg = s3.try_put( T(i) );
33151c0b2f7Stbbdev         CHECK_MESSAGE( msg == true, "" );
33251c0b2f7Stbbdev     }
33351c0b2f7Stbbdev 
33451c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
33551c0b2f7Stbbdev         j = bogus_value;
33651c0b2f7Stbbdev         CHECK_MESSAGE(wait_try_get( g, s5, j ) == true, "");
33751c0b2f7Stbbdev         CHECK_MESSAGE( i == j, "" );
33851c0b2f7Stbbdev     }
33951c0b2f7Stbbdev     j = bogus_value;
34051c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s3, j ) == false, "" );
34151c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s4, j ) == false, "" );
34251c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s5, j ) == false, "" );
34351c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
34451c0b2f7Stbbdev 
34551c0b2f7Stbbdev     g.wait_for_all();
34651c0b2f7Stbbdev     tbb::flow::remove_edge( s3, s4 );
34751c0b2f7Stbbdev     CHECK_MESSAGE( s3.try_put( N ) == true, "" );
34851c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s4, j ) == false, "" );
34951c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
35051c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s5, j ) == false, "" );
35151c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
35251c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s3, j ) == true, "" );
35351c0b2f7Stbbdev     CHECK_MESSAGE( j == N, "" );
35451c0b2f7Stbbdev 
35551c0b2f7Stbbdev     //
35651c0b2f7Stbbdev     // Chained reverse-order simple puts and gets
35751c0b2f7Stbbdev     //
35851c0b2f7Stbbdev 
35951c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s6(g, seq_inspector<T>());
36051c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s7(g, seq_inspector<T>());
36151c0b2f7Stbbdev     tbb::flow::sequencer_node<T> s8(g, seq_inspector<T>());
36251c0b2f7Stbbdev     tbb::flow::make_edge( s6, s7 );
36351c0b2f7Stbbdev     tbb::flow::make_edge( s7, s8 );
36451c0b2f7Stbbdev 
36551c0b2f7Stbbdev     for (int i = N-1; i >= 0; --i) {
36651c0b2f7Stbbdev         bool msg = s6.try_put( T(i) );
36751c0b2f7Stbbdev         CHECK_MESSAGE( msg == true, "" );
36851c0b2f7Stbbdev     }
36951c0b2f7Stbbdev 
37051c0b2f7Stbbdev     for (int i = 0; i < N; ++i) {
37151c0b2f7Stbbdev         j = bogus_value;
37251c0b2f7Stbbdev         CHECK_MESSAGE( wait_try_get( g, s8, j ) == true, "" );
37351c0b2f7Stbbdev         CHECK_MESSAGE( i == j, "" );
37451c0b2f7Stbbdev     }
37551c0b2f7Stbbdev     j = bogus_value;
37651c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s6, j ) == false, "" );
37751c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s7, j ) == false, "" );
37851c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s8, j ) == false, "" );
37951c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
38051c0b2f7Stbbdev 
38151c0b2f7Stbbdev     g.wait_for_all();
38251c0b2f7Stbbdev     tbb::flow::remove_edge( s6, s7 );
38351c0b2f7Stbbdev     CHECK_MESSAGE( s6.try_put( N ) == true, "" );
38451c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s7, j ) == false, "" );
38551c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
38651c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s8, j ) == false, "" );
38751c0b2f7Stbbdev     CHECK_MESSAGE( j == bogus_value, "" );
38851c0b2f7Stbbdev     CHECK_MESSAGE( wait_try_get( g, s6, j ) == true, "" );
38951c0b2f7Stbbdev     CHECK_MESSAGE( j == N, "" );
39051c0b2f7Stbbdev 
39151c0b2f7Stbbdev     return 0;
39251c0b2f7Stbbdev }
39351c0b2f7Stbbdev 
39451c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
39551c0b2f7Stbbdev #include <array>
39651c0b2f7Stbbdev #include <vector>
39751c0b2f7Stbbdev void test_follows_and_precedes_api() {
39851c0b2f7Stbbdev     std::array<int, 3> messages_for_follows = { {0, 1, 2} };
39951c0b2f7Stbbdev     std::vector<int> messages_for_precedes = {0, 1, 2};
40051c0b2f7Stbbdev 
40151c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
40251c0b2f7Stbbdev         <int, tbb::flow::sequencer_node<int>>
403*478de5b1Stbbdev         (messages_for_follows, [](const int& i) -> std::size_t { return i; });
40451c0b2f7Stbbdev 
40551c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
40651c0b2f7Stbbdev         <int, tbb::flow::sequencer_node<int>>
407*478de5b1Stbbdev         (messages_for_precedes, [](const int& i) -> std::size_t { return i; });
40851c0b2f7Stbbdev }
40951c0b2f7Stbbdev #endif
41051c0b2f7Stbbdev 
41151c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
41251c0b2f7Stbbdev template <typename Body>
41351c0b2f7Stbbdev void test_deduction_guides_common(Body body) {
41451c0b2f7Stbbdev     using namespace tbb::flow;
41551c0b2f7Stbbdev     graph g;
41651c0b2f7Stbbdev     broadcast_node<int> br(g);
41751c0b2f7Stbbdev 
41851c0b2f7Stbbdev     sequencer_node s1(g, body);
41951c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s1), sequencer_node<int>>);
42051c0b2f7Stbbdev 
42151c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
42251c0b2f7Stbbdev     sequencer_node s2(follows(br), body);
42351c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s2), sequencer_node<int>>);
42451c0b2f7Stbbdev #endif
42551c0b2f7Stbbdev 
42651c0b2f7Stbbdev     sequencer_node s3(s1);
42751c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s3), sequencer_node<int>>);
42851c0b2f7Stbbdev }
42951c0b2f7Stbbdev 
430*478de5b1Stbbdev std::size_t sequencer_body_f(const int&) { return 1; }
43151c0b2f7Stbbdev 
43251c0b2f7Stbbdev void test_deduction_guides() {
433*478de5b1Stbbdev     test_deduction_guides_common([](const int&)->std::size_t { return 1; });
434*478de5b1Stbbdev     test_deduction_guides_common([](const int&) mutable ->std::size_t { return 1; });
43551c0b2f7Stbbdev     test_deduction_guides_common(sequencer_body_f);
43651c0b2f7Stbbdev }
43751c0b2f7Stbbdev #endif
43851c0b2f7Stbbdev 
43951c0b2f7Stbbdev //! Test sequencer with various request orders and parallelism levels
44051c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
44151c0b2f7Stbbdev TEST_CASE("Serial and parallel test"){
44251c0b2f7Stbbdev     for (int p = 2; p <= 4; ++p) {
44351c0b2f7Stbbdev         tbb::task_arena arena(p);
44451c0b2f7Stbbdev         arena.execute(
44551c0b2f7Stbbdev             [&]() {
44651c0b2f7Stbbdev                 test_serial<int>();
44751c0b2f7Stbbdev                 test_parallel<int>(p);
44851c0b2f7Stbbdev             }
44951c0b2f7Stbbdev         );
45051c0b2f7Stbbdev 	}
45151c0b2f7Stbbdev }
45251c0b2f7Stbbdev 
45351c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
45451c0b2f7Stbbdev //! Test decution guides
45551c0b2f7Stbbdev //! \brief \ref requirement
45651c0b2f7Stbbdev TEST_CASE("Test follows and precedes API"){
45751c0b2f7Stbbdev     test_follows_and_precedes_api();
45851c0b2f7Stbbdev }
45951c0b2f7Stbbdev #endif
46051c0b2f7Stbbdev 
46151c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
46251c0b2f7Stbbdev //! Test decution guides
46351c0b2f7Stbbdev //! \brief \ref requirement
46451c0b2f7Stbbdev TEST_CASE("Test deduction guides"){
46551c0b2f7Stbbdev     test_deduction_guides();
46651c0b2f7Stbbdev }
46751c0b2f7Stbbdev #endif
468*478de5b1Stbbdev 
469*478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
470*478de5b1Stbbdev //! \brief \ref error_guessing
471*478de5b1Stbbdev TEST_CASE("constraints for sequencer_node object") {
472*478de5b1Stbbdev     struct Object : test_concepts::Copyable, test_concepts::CopyAssignable {};
473*478de5b1Stbbdev 
474*478de5b1Stbbdev     static_assert(utils::well_formed_instantiation<tbb::flow::sequencer_node, Object>);
475*478de5b1Stbbdev     static_assert(utils::well_formed_instantiation<tbb::flow::sequencer_node, int>);
476*478de5b1Stbbdev     static_assert(!utils::well_formed_instantiation<tbb::flow::sequencer_node, test_concepts::NonCopyable>);
477*478de5b1Stbbdev     static_assert(!utils::well_formed_instantiation<tbb::flow::sequencer_node, test_concepts::NonCopyAssignable>);
478*478de5b1Stbbdev }
479*478de5b1Stbbdev 
480*478de5b1Stbbdev template <typename T, typename Sequencer>
481*478de5b1Stbbdev concept can_call_sequencer_node_ctor = requires( tbb::flow::graph& graph, Sequencer seq,
482*478de5b1Stbbdev                                                  tbb::flow::buffer_node<int>& f ) {
483*478de5b1Stbbdev     tbb::flow::sequencer_node<T>(graph, seq);
484*478de5b1Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
485*478de5b1Stbbdev     tbb::flow::sequencer_node<T>(tbb::flow::follows(f), seq);
486*478de5b1Stbbdev #endif
487*478de5b1Stbbdev };
488*478de5b1Stbbdev 
489*478de5b1Stbbdev //! \brief \ref error_guessing
490*478de5b1Stbbdev TEST_CASE("constraints for sequencer_node sequencer") {
491*478de5b1Stbbdev     using type = int;
492*478de5b1Stbbdev     using namespace test_concepts::sequencer;
493*478de5b1Stbbdev 
494*478de5b1Stbbdev     static_assert(can_call_sequencer_node_ctor<type, Correct<type>>);
495*478de5b1Stbbdev     static_assert(!can_call_sequencer_node_ctor<type, NonCopyable<type>>);
496*478de5b1Stbbdev     static_assert(!can_call_sequencer_node_ctor<type, NonDestructible<type>>);
497*478de5b1Stbbdev     static_assert(!can_call_sequencer_node_ctor<type, NoOperatorRoundBrackets<type>>);
498*478de5b1Stbbdev     static_assert(!can_call_sequencer_node_ctor<type, WrongInputOperatorRoundBrackets<type>>);
499*478de5b1Stbbdev     static_assert(!can_call_sequencer_node_ctor<type, WrongReturnOperatorRoundBrackets<type>>);
500*478de5b1Stbbdev }
501*478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
502