xref: /oneTBB/test/tbb/test_limiter_node.cpp (revision b15aabb3)
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/utils_assert.h"
2851c0b2f7Stbbdev #include "common/test_follows_and_precedes_api.h"
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev #include <atomic>
3151c0b2f7Stbbdev 
3251c0b2f7Stbbdev 
3351c0b2f7Stbbdev //! \file test_limiter_node.cpp
3451c0b2f7Stbbdev //! \brief Test for [flow_graph.limiter_node] specification
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev 
3751c0b2f7Stbbdev const int L = 10;
3851c0b2f7Stbbdev const int N = 1000;
3951c0b2f7Stbbdev 
4051c0b2f7Stbbdev using tbb::detail::d1::SUCCESSFULLY_ENQUEUED;
4151c0b2f7Stbbdev using tbb::detail::d1::graph_task;
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev template< typename T >
4451c0b2f7Stbbdev struct serial_receiver : public tbb::flow::receiver<T>, utils::NoAssign {
4551c0b2f7Stbbdev    T next_value;
4651c0b2f7Stbbdev    tbb::flow::graph& my_graph;
4751c0b2f7Stbbdev 
4851c0b2f7Stbbdev    serial_receiver(tbb::flow::graph& g) : next_value(T(0)), my_graph(g) {}
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev    graph_task* try_put_task( const T &v ) override {
5151c0b2f7Stbbdev        CHECK_MESSAGE( next_value++  == v, "" );
5251c0b2f7Stbbdev        return const_cast<graph_task*>(SUCCESSFULLY_ENQUEUED);
5351c0b2f7Stbbdev    }
5451c0b2f7Stbbdev 
5551c0b2f7Stbbdev     tbb::flow::graph& graph_reference() const override {
5651c0b2f7Stbbdev         return my_graph;
5751c0b2f7Stbbdev     }
5851c0b2f7Stbbdev };
5951c0b2f7Stbbdev 
6051c0b2f7Stbbdev template< typename T >
6151c0b2f7Stbbdev struct parallel_receiver : public tbb::flow::receiver<T>, utils::NoAssign {
6251c0b2f7Stbbdev 
6351c0b2f7Stbbdev     std::atomic<int> my_count;
6451c0b2f7Stbbdev     tbb::flow::graph& my_graph;
6551c0b2f7Stbbdev 
6651c0b2f7Stbbdev     parallel_receiver(tbb::flow::graph& g) : my_graph(g) { my_count = 0; }
6751c0b2f7Stbbdev 
6851c0b2f7Stbbdev     graph_task* try_put_task( const T &/*v*/ ) override {
6951c0b2f7Stbbdev        ++my_count;
7051c0b2f7Stbbdev        return const_cast<graph_task*>(SUCCESSFULLY_ENQUEUED);
7151c0b2f7Stbbdev     }
7251c0b2f7Stbbdev 
7351c0b2f7Stbbdev     tbb::flow::graph& graph_reference() const override {
7451c0b2f7Stbbdev         return my_graph;
7551c0b2f7Stbbdev     }
7651c0b2f7Stbbdev };
7751c0b2f7Stbbdev 
7851c0b2f7Stbbdev template< typename T >
7951c0b2f7Stbbdev struct empty_sender : public tbb::flow::sender<T> {
8051c0b2f7Stbbdev         typedef typename tbb::flow::sender<T>::successor_type successor_type;
8151c0b2f7Stbbdev 
8251c0b2f7Stbbdev         bool register_successor( successor_type & ) override { return false; }
8351c0b2f7Stbbdev         bool remove_successor( successor_type & ) override { return false; }
8451c0b2f7Stbbdev };
8551c0b2f7Stbbdev 
8651c0b2f7Stbbdev 
8751c0b2f7Stbbdev template< typename T >
8851c0b2f7Stbbdev struct put_body : utils::NoAssign {
8951c0b2f7Stbbdev 
9051c0b2f7Stbbdev     tbb::flow::limiter_node<T> &my_lim;
9151c0b2f7Stbbdev     std::atomic<int> &my_accept_count;
9251c0b2f7Stbbdev 
9351c0b2f7Stbbdev     put_body( tbb::flow::limiter_node<T> &lim, std::atomic<int> &accept_count ) :
9451c0b2f7Stbbdev         my_lim(lim), my_accept_count(accept_count) {}
9551c0b2f7Stbbdev 
9651c0b2f7Stbbdev     void operator()( int ) const {
9751c0b2f7Stbbdev         for ( int i = 0; i < L; ++i ) {
9851c0b2f7Stbbdev             bool msg = my_lim.try_put( T(i) );
9951c0b2f7Stbbdev             if ( msg == true )
10051c0b2f7Stbbdev                ++my_accept_count;
10151c0b2f7Stbbdev         }
10251c0b2f7Stbbdev     }
10351c0b2f7Stbbdev };
10451c0b2f7Stbbdev 
10551c0b2f7Stbbdev template< typename T >
10651c0b2f7Stbbdev struct put_dec_body : utils::NoAssign {
10751c0b2f7Stbbdev 
10851c0b2f7Stbbdev     tbb::flow::limiter_node<T> &my_lim;
10951c0b2f7Stbbdev     std::atomic<int> &my_accept_count;
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     put_dec_body( tbb::flow::limiter_node<T> &lim, std::atomic<int> &accept_count ) :
11251c0b2f7Stbbdev         my_lim(lim), my_accept_count(accept_count) {}
11351c0b2f7Stbbdev 
11451c0b2f7Stbbdev     void operator()( int ) const {
11551c0b2f7Stbbdev         int local_accept_count = 0;
11651c0b2f7Stbbdev         while ( local_accept_count < N ) {
11751c0b2f7Stbbdev             bool msg = my_lim.try_put( T(local_accept_count) );
11851c0b2f7Stbbdev             if ( msg == true ) {
11951c0b2f7Stbbdev                 ++local_accept_count;
12051c0b2f7Stbbdev                 ++my_accept_count;
12149e08aacStbbdev                 my_lim.decrementer().try_put( tbb::flow::continue_msg() );
12251c0b2f7Stbbdev             }
12351c0b2f7Stbbdev         }
12451c0b2f7Stbbdev     }
12551c0b2f7Stbbdev 
12651c0b2f7Stbbdev };
12751c0b2f7Stbbdev 
12851c0b2f7Stbbdev template< typename T >
12951c0b2f7Stbbdev void test_puts_with_decrements( int num_threads, tbb::flow::limiter_node< T >& lim , tbb::flow::graph& g) {
13051c0b2f7Stbbdev     parallel_receiver<T> r(g);
13151c0b2f7Stbbdev     empty_sender< tbb::flow::continue_msg > s;
13251c0b2f7Stbbdev     std::atomic<int> accept_count;
13351c0b2f7Stbbdev     accept_count = 0;
13451c0b2f7Stbbdev     tbb::flow::make_edge( lim, r );
13549e08aacStbbdev     tbb::flow::make_edge(s, lim.decrementer());
13651c0b2f7Stbbdev 
13751c0b2f7Stbbdev     // test puts with decrements
13851c0b2f7Stbbdev     utils::NativeParallelFor( num_threads, put_dec_body<T>(lim, accept_count) );
13951c0b2f7Stbbdev     int c = accept_count;
14051c0b2f7Stbbdev     CHECK_MESSAGE( c == N*num_threads, "" );
14151c0b2f7Stbbdev     CHECK_MESSAGE( r.my_count == N*num_threads, "" );
14251c0b2f7Stbbdev }
14351c0b2f7Stbbdev 
14451c0b2f7Stbbdev //
14551c0b2f7Stbbdev // Tests
14651c0b2f7Stbbdev //
14751c0b2f7Stbbdev // limiter only forwards below the limit, multiple parallel senders / single receiver
14851c0b2f7Stbbdev // multiple parallel senders that put to decrement at each accept, limiter accepts new messages
14951c0b2f7Stbbdev //
15051c0b2f7Stbbdev //
15151c0b2f7Stbbdev template< typename T >
15251c0b2f7Stbbdev int test_parallel(int num_threads) {
15351c0b2f7Stbbdev 
15451c0b2f7Stbbdev    // test puts with no decrements
15551c0b2f7Stbbdev    for ( int i = 0; i < L; ++i ) {
15651c0b2f7Stbbdev        tbb::flow::graph g;
15751c0b2f7Stbbdev        tbb::flow::limiter_node< T > lim(g, i);
15851c0b2f7Stbbdev        parallel_receiver<T> r(g);
15951c0b2f7Stbbdev        std::atomic<int> accept_count;
16051c0b2f7Stbbdev        accept_count = 0;
16151c0b2f7Stbbdev        tbb::flow::make_edge( lim, r );
16251c0b2f7Stbbdev        // test puts with no decrements
16351c0b2f7Stbbdev        utils::NativeParallelFor( num_threads, put_body<T>(lim, accept_count) );
16451c0b2f7Stbbdev        g.wait_for_all();
16551c0b2f7Stbbdev        int c = accept_count;
16651c0b2f7Stbbdev        CHECK_MESSAGE( c == i, "" );
16751c0b2f7Stbbdev    }
16851c0b2f7Stbbdev 
16951c0b2f7Stbbdev    // test puts with decrements
17051c0b2f7Stbbdev    for ( int i = 1; i < L; ++i ) {
17151c0b2f7Stbbdev        tbb::flow::graph g;
17251c0b2f7Stbbdev        tbb::flow::limiter_node< T > lim(g, i);
17351c0b2f7Stbbdev        test_puts_with_decrements(num_threads, lim, g);
17451c0b2f7Stbbdev        tbb::flow::limiter_node< T > lim_copy( lim );
17551c0b2f7Stbbdev        test_puts_with_decrements(num_threads, lim_copy, g);
17651c0b2f7Stbbdev    }
17751c0b2f7Stbbdev 
17851c0b2f7Stbbdev    return 0;
17951c0b2f7Stbbdev }
18051c0b2f7Stbbdev 
18151c0b2f7Stbbdev //
18251c0b2f7Stbbdev // Tests
18351c0b2f7Stbbdev //
18451c0b2f7Stbbdev // limiter only forwards below the limit, single sender / single receiver
18551c0b2f7Stbbdev // at reject, a put to decrement, will cause next message to be accepted
18651c0b2f7Stbbdev //
18751c0b2f7Stbbdev template< typename T >
18851c0b2f7Stbbdev int test_serial() {
18951c0b2f7Stbbdev 
19051c0b2f7Stbbdev    // test puts with no decrements
19151c0b2f7Stbbdev    for ( int i = 0; i < L; ++i ) {
19251c0b2f7Stbbdev        tbb::flow::graph g;
19351c0b2f7Stbbdev        tbb::flow::limiter_node< T > lim(g, i);
19451c0b2f7Stbbdev        serial_receiver<T> r(g);
19551c0b2f7Stbbdev        tbb::flow::make_edge( lim, r );
19651c0b2f7Stbbdev        for ( int j = 0; j < L; ++j ) {
19751c0b2f7Stbbdev            bool msg = lim.try_put( T(j) );
19851c0b2f7Stbbdev            CHECK_MESSAGE( (( j < i && msg == true ) || ( j >= i && msg == false )), "" );
19951c0b2f7Stbbdev        }
20051c0b2f7Stbbdev        g.wait_for_all();
20151c0b2f7Stbbdev    }
20251c0b2f7Stbbdev 
20351c0b2f7Stbbdev    // test puts with decrements
20451c0b2f7Stbbdev    for ( int i = 1; i < L; ++i ) {
20551c0b2f7Stbbdev        tbb::flow::graph g;
20651c0b2f7Stbbdev        tbb::flow::limiter_node< T > lim(g, i);
20751c0b2f7Stbbdev        serial_receiver<T> r(g);
20851c0b2f7Stbbdev        empty_sender< tbb::flow::continue_msg > s;
20951c0b2f7Stbbdev        tbb::flow::make_edge( lim, r );
21049e08aacStbbdev        tbb::flow::make_edge(s, lim.decrementer());
21151c0b2f7Stbbdev        for ( int j = 0; j < N; ++j ) {
21251c0b2f7Stbbdev            bool msg = lim.try_put( T(j) );
21351c0b2f7Stbbdev            CHECK_MESSAGE( (( j < i && msg == true ) || ( j >= i && msg == false )), "" );
21451c0b2f7Stbbdev            if ( msg == false ) {
21549e08aacStbbdev                lim.decrementer().try_put( tbb::flow::continue_msg() );
21651c0b2f7Stbbdev                msg = lim.try_put( T(j) );
21751c0b2f7Stbbdev                CHECK_MESSAGE( msg == true, "" );
21851c0b2f7Stbbdev            }
21951c0b2f7Stbbdev        }
22051c0b2f7Stbbdev    }
22151c0b2f7Stbbdev    return 0;
22251c0b2f7Stbbdev }
22351c0b2f7Stbbdev 
22451c0b2f7Stbbdev // reported bug in limiter (http://software.intel.com/en-us/comment/1752355)
22551c0b2f7Stbbdev #define DECREMENT_OUTPUT 1  // the port number of the decrement output of the multifunction_node
22651c0b2f7Stbbdev #define LIMITER_OUTPUT 0    // port number of the integer output
22751c0b2f7Stbbdev 
22851c0b2f7Stbbdev typedef tbb::flow::multifunction_node<int, std::tuple<int,tbb::flow::continue_msg> > mfnode_type;
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev std::atomic<size_t> emit_count;
23151c0b2f7Stbbdev std::atomic<size_t> emit_sum;
23251c0b2f7Stbbdev std::atomic<size_t> receive_count;
23351c0b2f7Stbbdev std::atomic<size_t> receive_sum;
23451c0b2f7Stbbdev 
23551c0b2f7Stbbdev struct mfnode_body {
23651c0b2f7Stbbdev     int max_cnt;
23751c0b2f7Stbbdev     std::atomic<int>* my_cnt;
23851c0b2f7Stbbdev     mfnode_body(const int& _max, std::atomic<int> &_my) : max_cnt(_max), my_cnt(&_my)  { }
23951c0b2f7Stbbdev     void operator()(const int &/*in*/, mfnode_type::output_ports_type &out) {
24051c0b2f7Stbbdev         int lcnt = ++(*my_cnt);
24151c0b2f7Stbbdev         if(lcnt > max_cnt) {
24251c0b2f7Stbbdev             return;
24351c0b2f7Stbbdev         }
24451c0b2f7Stbbdev         // put one continue_msg to the decrement of the limiter.
24551c0b2f7Stbbdev         if(!std::get<DECREMENT_OUTPUT>(out).try_put(tbb::flow::continue_msg())) {
24651c0b2f7Stbbdev             CHECK_MESSAGE( (false),"Unexpected rejection of decrement");
24751c0b2f7Stbbdev         }
24851c0b2f7Stbbdev         {
24951c0b2f7Stbbdev             // put messages to the input of the limiter_node until it rejects.
25051c0b2f7Stbbdev             while( std::get<LIMITER_OUTPUT>(out).try_put(lcnt) ) {
25151c0b2f7Stbbdev                 emit_sum += lcnt;
25251c0b2f7Stbbdev                 ++emit_count;
25351c0b2f7Stbbdev             }
25451c0b2f7Stbbdev         }
25551c0b2f7Stbbdev     }
25651c0b2f7Stbbdev };
25751c0b2f7Stbbdev 
25851c0b2f7Stbbdev struct fn_body {
25951c0b2f7Stbbdev     int operator()(const int &in) {
26051c0b2f7Stbbdev         receive_sum += in;
26151c0b2f7Stbbdev         ++receive_count;
26251c0b2f7Stbbdev         return in;
26351c0b2f7Stbbdev     }
26451c0b2f7Stbbdev };
26551c0b2f7Stbbdev 
26651c0b2f7Stbbdev //                   +------------+
26751c0b2f7Stbbdev //    +---------+    |            v
26851c0b2f7Stbbdev //    | mf_node |0---+       +----------+          +----------+
26951c0b2f7Stbbdev // +->|         |1---------->| lim_node |--------->| fn_node  |--+
27051c0b2f7Stbbdev // |  +---------+            +----------+          +----------+  |
27151c0b2f7Stbbdev // |                                                             |
27251c0b2f7Stbbdev // |                                                             |
27351c0b2f7Stbbdev // +-------------------------------------------------------------+
27451c0b2f7Stbbdev //
27551c0b2f7Stbbdev void
27651c0b2f7Stbbdev test_multifunction_to_limiter(int _max, int _nparallel) {
27751c0b2f7Stbbdev     tbb::flow::graph g;
27851c0b2f7Stbbdev     emit_count = 0;
27951c0b2f7Stbbdev     emit_sum = 0;
28051c0b2f7Stbbdev     receive_count = 0;
28151c0b2f7Stbbdev     receive_sum = 0;
28251c0b2f7Stbbdev     std::atomic<int> local_cnt;
28351c0b2f7Stbbdev     local_cnt = 0;
28451c0b2f7Stbbdev     mfnode_type mf_node(g, tbb::flow::unlimited, mfnode_body(_max, local_cnt));
28551c0b2f7Stbbdev     tbb::flow::function_node<int, int> fn_node(g, tbb::flow::unlimited, fn_body());
28651c0b2f7Stbbdev     tbb::flow::limiter_node<int> lim_node(g, _nparallel);
28751c0b2f7Stbbdev     tbb::flow::make_edge(tbb::flow::output_port<LIMITER_OUTPUT>(mf_node), lim_node);
28849e08aacStbbdev     tbb::flow::make_edge(tbb::flow::output_port<DECREMENT_OUTPUT>(mf_node), lim_node.decrementer());
28951c0b2f7Stbbdev     tbb::flow::make_edge(lim_node, fn_node);
29051c0b2f7Stbbdev     tbb::flow::make_edge(fn_node, mf_node);
29151c0b2f7Stbbdev 
29251c0b2f7Stbbdev     mf_node.try_put(1);
29351c0b2f7Stbbdev     g.wait_for_all();
29451c0b2f7Stbbdev     CHECK_MESSAGE( (emit_count == receive_count), "counts do not match");
29551c0b2f7Stbbdev     CHECK_MESSAGE( (emit_sum == receive_sum), "sums do not match");
29651c0b2f7Stbbdev 
29751c0b2f7Stbbdev     // reset, test again
29851c0b2f7Stbbdev     g.reset();
29951c0b2f7Stbbdev     emit_count = 0;
30051c0b2f7Stbbdev     emit_sum = 0;
30151c0b2f7Stbbdev     receive_count = 0;
30251c0b2f7Stbbdev     receive_sum = 0;
30351c0b2f7Stbbdev     local_cnt = 0;;
30451c0b2f7Stbbdev     mf_node.try_put(1);
30551c0b2f7Stbbdev     g.wait_for_all();
30651c0b2f7Stbbdev     CHECK_MESSAGE( (emit_count == receive_count), "counts do not match");
30751c0b2f7Stbbdev     CHECK_MESSAGE( (emit_sum == receive_sum), "sums do not match");
30851c0b2f7Stbbdev }
30951c0b2f7Stbbdev 
31051c0b2f7Stbbdev 
31151c0b2f7Stbbdev void
31251c0b2f7Stbbdev test_continue_msg_reception() {
31351c0b2f7Stbbdev     tbb::flow::graph g;
31451c0b2f7Stbbdev     tbb::flow::limiter_node<int> ln(g,2);
31551c0b2f7Stbbdev     tbb::flow::queue_node<int>   qn(g);
31651c0b2f7Stbbdev     tbb::flow::make_edge(ln, qn);
31749e08aacStbbdev     ln.decrementer().try_put(tbb::flow::continue_msg());
31851c0b2f7Stbbdev     ln.try_put(42);
31951c0b2f7Stbbdev     g.wait_for_all();
32051c0b2f7Stbbdev     int outint;
32151c0b2f7Stbbdev     CHECK_MESSAGE( (qn.try_get(outint) && outint == 42), "initial put to decrement stops node");
32251c0b2f7Stbbdev }
32351c0b2f7Stbbdev 
32451c0b2f7Stbbdev 
32551c0b2f7Stbbdev //
32651c0b2f7Stbbdev // This test ascertains that if a message is not successfully put
32751c0b2f7Stbbdev // to a successor, the message is not dropped but released.
32851c0b2f7Stbbdev //
32951c0b2f7Stbbdev 
33051c0b2f7Stbbdev void test_reserve_release_messages() {
33151c0b2f7Stbbdev     using namespace tbb::flow;
33251c0b2f7Stbbdev     graph g;
33351c0b2f7Stbbdev 
33451c0b2f7Stbbdev     //making two queue_nodes: one broadcast_node and one limiter_node
33551c0b2f7Stbbdev     queue_node<int> input_queue(g);
33651c0b2f7Stbbdev     queue_node<int> output_queue(g);
33751c0b2f7Stbbdev     broadcast_node<int> broad(g);
33851c0b2f7Stbbdev     limiter_node<int, int> limit(g,2); //threshold of 2
33951c0b2f7Stbbdev 
34051c0b2f7Stbbdev     //edges
34151c0b2f7Stbbdev     make_edge(input_queue, limit);
34251c0b2f7Stbbdev     make_edge(limit, output_queue);
34349e08aacStbbdev     make_edge(broad,limit.decrementer());
34451c0b2f7Stbbdev 
34551c0b2f7Stbbdev     int list[4] = {19, 33, 72, 98}; //list to be put to the input queue
34651c0b2f7Stbbdev 
34751c0b2f7Stbbdev     input_queue.try_put(list[0]); // succeeds
34851c0b2f7Stbbdev     input_queue.try_put(list[1]); // succeeds
34951c0b2f7Stbbdev     input_queue.try_put(list[2]); // fails, stored in upstream buffer
35051c0b2f7Stbbdev     g.wait_for_all();
35151c0b2f7Stbbdev 
35251c0b2f7Stbbdev     remove_edge(limit, output_queue); //remove successor
35351c0b2f7Stbbdev 
35451c0b2f7Stbbdev     //sending message to the decrement port of the limiter
35551c0b2f7Stbbdev     broad.try_put(1); //failed message retrieved.
35651c0b2f7Stbbdev     g.wait_for_all();
35751c0b2f7Stbbdev 
35851c0b2f7Stbbdev     make_edge(limit, output_queue); //putting the successor back
35951c0b2f7Stbbdev 
36051c0b2f7Stbbdev     broad.try_put(1);  //drop the count
36151c0b2f7Stbbdev 
36251c0b2f7Stbbdev     input_queue.try_put(list[3]);  //success
36351c0b2f7Stbbdev     g.wait_for_all();
36451c0b2f7Stbbdev 
36551c0b2f7Stbbdev     int var=0;
36651c0b2f7Stbbdev 
36751c0b2f7Stbbdev     for (int i=0; i<4; i++) {
36851c0b2f7Stbbdev         output_queue.try_get(var);
36951c0b2f7Stbbdev         CHECK_MESSAGE( (var==list[i]), "some data dropped, input does not match output");
37051c0b2f7Stbbdev         g.wait_for_all();
37151c0b2f7Stbbdev     }
37251c0b2f7Stbbdev }
37351c0b2f7Stbbdev 
37451c0b2f7Stbbdev void test_decrementer() {
37551c0b2f7Stbbdev     const int threshold = 5;
37651c0b2f7Stbbdev     tbb::flow::graph g;
37751c0b2f7Stbbdev     tbb::flow::limiter_node<int, int> limit(g, threshold);
37851c0b2f7Stbbdev     tbb::flow::queue_node<int> queue(g);
37951c0b2f7Stbbdev     make_edge(limit, queue);
38051c0b2f7Stbbdev     int m = 0;
38151c0b2f7Stbbdev     CHECK_MESSAGE( ( limit.try_put( m++ )), "Newly constructed limiter node does not accept message." );
38249e08aacStbbdev     CHECK_MESSAGE( limit.decrementer().try_put( -threshold ), // close limiter's gate
38351c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message." );
38451c0b2f7Stbbdev     CHECK_MESSAGE( ( !limit.try_put( m++ )), "Closed limiter node's accepts message." );
38549e08aacStbbdev     CHECK_MESSAGE( limit.decrementer().try_put( threshold + 5 ),  // open limiter's gate
38651c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message." );
38751c0b2f7Stbbdev     for( int i = 0; i < threshold; ++i )
38851c0b2f7Stbbdev         CHECK_MESSAGE( ( limit.try_put( m++ )), "Limiter node does not accept message while open." );
38951c0b2f7Stbbdev     CHECK_MESSAGE( ( !limit.try_put( m )), "Limiter node's gate is not closed." );
39051c0b2f7Stbbdev     g.wait_for_all();
39151c0b2f7Stbbdev     int expected[] = {0, 2, 3, 4, 5, 6};
39251c0b2f7Stbbdev     int actual = -1; m = 0;
39351c0b2f7Stbbdev     while( queue.try_get(actual) )
39451c0b2f7Stbbdev         CHECK_MESSAGE( actual == expected[m++], "" );
39551c0b2f7Stbbdev     CHECK_MESSAGE( ( sizeof(expected) / sizeof(expected[0]) == m), "Not all messages have been processed." );
39651c0b2f7Stbbdev     g.wait_for_all();
39751c0b2f7Stbbdev 
39851c0b2f7Stbbdev     const size_t threshold2 = size_t(-1);
39951c0b2f7Stbbdev     tbb::flow::limiter_node<int, long long> limit2(g, threshold2);
40051c0b2f7Stbbdev     make_edge(limit2, queue);
40151c0b2f7Stbbdev     CHECK_MESSAGE( ( limit2.try_put( 1 )), "Newly constructed limiter node does not accept message." );
40251c0b2f7Stbbdev     long long decrement_value = (long long)( size_t(-1)/2 );
40349e08aacStbbdev     CHECK_MESSAGE( limit2.decrementer().try_put( -decrement_value ),
40451c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message" );
40551c0b2f7Stbbdev     CHECK_MESSAGE( ( limit2.try_put( 2 )), "Limiter's gate should not be closed yet." );
40649e08aacStbbdev     CHECK_MESSAGE( limit2.decrementer().try_put( -decrement_value ),
40751c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message" );
40851c0b2f7Stbbdev     CHECK_MESSAGE( ( !limit2.try_put( 3 )), "Overflow happened for internal counter." );
40951c0b2f7Stbbdev     int expected2[] = {1, 2};
41051c0b2f7Stbbdev     actual = -1; m = 0;
41151c0b2f7Stbbdev     while( queue.try_get(actual) )
41251c0b2f7Stbbdev         CHECK_MESSAGE( actual == expected2[m++], "" );
41351c0b2f7Stbbdev     CHECK_MESSAGE( ( sizeof(expected2) / sizeof(expected2[0]) == m), "Not all messages have been processed." );
41451c0b2f7Stbbdev     g.wait_for_all();
41551c0b2f7Stbbdev }
41651c0b2f7Stbbdev 
41751c0b2f7Stbbdev void test_try_put_without_successors() {
41851c0b2f7Stbbdev     tbb::flow::graph g;
41951c0b2f7Stbbdev     std::size_t try_put_num{3};
42051c0b2f7Stbbdev     tbb::flow::buffer_node<int> bn(g);
42151c0b2f7Stbbdev     tbb::flow::limiter_node<int> ln(g, try_put_num);
42251c0b2f7Stbbdev     tbb::flow::make_edge(bn, ln);
42351c0b2f7Stbbdev     std::size_t i = 1;
42451c0b2f7Stbbdev     for (; i <= try_put_num; i++)
42551c0b2f7Stbbdev         bn.try_put(i);
42651c0b2f7Stbbdev 
42751c0b2f7Stbbdev     std::atomic<std::size_t> counter{0};
42851c0b2f7Stbbdev     tbb::flow::function_node<int, int> fn(g, tbb::flow::unlimited,
42951c0b2f7Stbbdev         [&](int input) {
43051c0b2f7Stbbdev             counter += input;
43151c0b2f7Stbbdev             return int{};
43251c0b2f7Stbbdev         }
43351c0b2f7Stbbdev     );
43451c0b2f7Stbbdev     tbb::flow::make_edge(ln, fn);
43551c0b2f7Stbbdev     g.wait_for_all();
43651c0b2f7Stbbdev     CHECK((counter == i * try_put_num / 2));
43751c0b2f7Stbbdev 
43851c0b2f7Stbbdev     // Check the lost message
43951c0b2f7Stbbdev     tbb::flow::remove_edge(bn, ln);
44049e08aacStbbdev     ln.decrementer().try_put(tbb::flow::continue_msg());
44151c0b2f7Stbbdev     bn.try_put(try_put_num + 1);
44251c0b2f7Stbbdev     g.wait_for_all();
44351c0b2f7Stbbdev     CHECK((counter == i * try_put_num / 2));
44451c0b2f7Stbbdev 
44551c0b2f7Stbbdev }
44651c0b2f7Stbbdev 
44751c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
44851c0b2f7Stbbdev #include <array>
44951c0b2f7Stbbdev #include <vector>
45051c0b2f7Stbbdev void test_follows_and_precedes_api() {
45151c0b2f7Stbbdev     using msg_t = tbb::flow::continue_msg;
45251c0b2f7Stbbdev 
45351c0b2f7Stbbdev     std::array<msg_t, 3> messages_for_follows= { {msg_t(), msg_t(), msg_t()} };
45451c0b2f7Stbbdev     std::vector<msg_t> messages_for_precedes = {msg_t()};
45551c0b2f7Stbbdev 
45651c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
45751c0b2f7Stbbdev         <msg_t, tbb::flow::limiter_node<msg_t, msg_t>>(messages_for_follows, 1000);
45851c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
45951c0b2f7Stbbdev         <msg_t, tbb::flow::limiter_node<msg_t, msg_t>>(messages_for_precedes, 1000);
46051c0b2f7Stbbdev 
46151c0b2f7Stbbdev }
46251c0b2f7Stbbdev #endif
46351c0b2f7Stbbdev 
46451c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
46551c0b2f7Stbbdev void test_deduction_guides() {
46651c0b2f7Stbbdev     using namespace tbb::flow;
46751c0b2f7Stbbdev 
46851c0b2f7Stbbdev     graph g;
46951c0b2f7Stbbdev     broadcast_node<int> br(g);
47051c0b2f7Stbbdev     limiter_node<int> l0(g, 100);
47151c0b2f7Stbbdev 
47251c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
47351c0b2f7Stbbdev     limiter_node l1(follows(br), 100);
47451c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(l1), limiter_node<int>>);
47551c0b2f7Stbbdev 
47651c0b2f7Stbbdev     limiter_node l2(precedes(br), 100);
47751c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(l2), limiter_node<int>>);
47851c0b2f7Stbbdev #endif
47951c0b2f7Stbbdev 
48051c0b2f7Stbbdev     limiter_node l3(l0);
48151c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(l3), limiter_node<int>>);
48251c0b2f7Stbbdev }
48351c0b2f7Stbbdev #endif
48451c0b2f7Stbbdev 
48551c0b2f7Stbbdev //! Test puts on limiter_node with decrements and varying parallelism levels
48651c0b2f7Stbbdev //! \brief \ref error_guessing
48751c0b2f7Stbbdev TEST_CASE("Serial and parallel tests") {
48851c0b2f7Stbbdev     for (unsigned i = 1; i <= 2 * utils::MaxThread; ++i) {
48951c0b2f7Stbbdev         tbb::task_arena arena(i);
49051c0b2f7Stbbdev         arena.execute(
49151c0b2f7Stbbdev             [i]() {
49251c0b2f7Stbbdev                 test_serial<int>();
49351c0b2f7Stbbdev                 test_parallel<int>(i);
49451c0b2f7Stbbdev             }
49551c0b2f7Stbbdev         );
49651c0b2f7Stbbdev     }
49751c0b2f7Stbbdev }
49851c0b2f7Stbbdev 
49951c0b2f7Stbbdev //! Test initial put of continue_msg on decrementer port does not stop message flow
50051c0b2f7Stbbdev //! \brief \ref error_guessing
50151c0b2f7Stbbdev TEST_CASE("Test continue_msg reception") {
50251c0b2f7Stbbdev     test_continue_msg_reception();
50351c0b2f7Stbbdev }
50451c0b2f7Stbbdev 
50551c0b2f7Stbbdev //! Test multifunction_node connected to limiter_node
50651c0b2f7Stbbdev //! \brief \ref error_guessing
50751c0b2f7Stbbdev TEST_CASE("Multifunction connected to limiter") {
50851c0b2f7Stbbdev     test_multifunction_to_limiter(30,3);
50951c0b2f7Stbbdev     test_multifunction_to_limiter(300,13);
51051c0b2f7Stbbdev     test_multifunction_to_limiter(3000,1);
51151c0b2f7Stbbdev }
51251c0b2f7Stbbdev 
51351c0b2f7Stbbdev //! Test message release if successor doesn't accept
51451c0b2f7Stbbdev //! \brief \ref requirement
51551c0b2f7Stbbdev TEST_CASE("Message is released if successor does not accept") {
51651c0b2f7Stbbdev     test_reserve_release_messages();
51751c0b2f7Stbbdev }
51851c0b2f7Stbbdev 
51951c0b2f7Stbbdev //! Test decrementer
52051c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
52151c0b2f7Stbbdev TEST_CASE("Decrementer") {
52251c0b2f7Stbbdev     test_decrementer();
52351c0b2f7Stbbdev }
52451c0b2f7Stbbdev 
52551c0b2f7Stbbdev //! Test try_put() without successor
52651c0b2f7Stbbdev //! \brief \ref error_guessing
52751c0b2f7Stbbdev TEST_CASE("Test try_put() without successors") {
52851c0b2f7Stbbdev     test_try_put_without_successors();
52951c0b2f7Stbbdev }
53051c0b2f7Stbbdev 
53151c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
53251c0b2f7Stbbdev //! Test follows and precedes API
53351c0b2f7Stbbdev //! \brief \ref error_guessing
53451c0b2f7Stbbdev TEST_CASE( "Support for follows and precedes API" ) {
53551c0b2f7Stbbdev     test_follows_and_precedes_api();
53651c0b2f7Stbbdev }
53751c0b2f7Stbbdev #endif
53851c0b2f7Stbbdev 
53951c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
54051c0b2f7Stbbdev //! Test deduction guides
54151c0b2f7Stbbdev //! \brief \ref requirement
54251c0b2f7Stbbdev TEST_CASE( "Deduction guides" ) {
54351c0b2f7Stbbdev     test_deduction_guides();
54451c0b2f7Stbbdev }
54551c0b2f7Stbbdev #endif
546