xref: /oneTBB/test/tbb/test_limiter_node.cpp (revision 25e2b1dd)
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 
17b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19b15aabb3Stbbdev #endif
20b15aabb3Stbbdev 
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 
358478de5b1Stbbdev #if __GNUC__ && __GNUC__ < 10 && !TBB_USE_DEBUG
359478de5b1Stbbdev     // Seemingly, GNU compiler generates incorrect code for the call of limiter.register_successor in release (-03)
360478de5b1Stbbdev     // The function pointer to make_edge workarounds the issue for unknown reason
361478de5b1Stbbdev     auto make_edge_ptr = make_edge<int>;
362478de5b1Stbbdev     make_edge_ptr(limit, output_queue); //putting the successor back
363478de5b1Stbbdev #else
36451c0b2f7Stbbdev     make_edge(limit, output_queue); //putting the successor back
365478de5b1Stbbdev #endif
36651c0b2f7Stbbdev 
36751c0b2f7Stbbdev     broad.try_put(1);  //drop the count
36851c0b2f7Stbbdev 
36951c0b2f7Stbbdev     input_queue.try_put(list[3]);  //success
37051c0b2f7Stbbdev     g.wait_for_all();
37151c0b2f7Stbbdev 
37251c0b2f7Stbbdev     int var=0;
37351c0b2f7Stbbdev 
37451c0b2f7Stbbdev     for (int i=0; i<4; i++) {
37551c0b2f7Stbbdev         output_queue.try_get(var);
37651c0b2f7Stbbdev         CHECK_MESSAGE( (var==list[i]), "some data dropped, input does not match output");
37751c0b2f7Stbbdev         g.wait_for_all();
37851c0b2f7Stbbdev     }
37951c0b2f7Stbbdev }
38051c0b2f7Stbbdev 
38151c0b2f7Stbbdev void test_decrementer() {
38251c0b2f7Stbbdev     const int threshold = 5;
38351c0b2f7Stbbdev     tbb::flow::graph g;
38451c0b2f7Stbbdev     tbb::flow::limiter_node<int, int> limit(g, threshold);
38551c0b2f7Stbbdev     tbb::flow::queue_node<int> queue(g);
38651c0b2f7Stbbdev     make_edge(limit, queue);
38751c0b2f7Stbbdev     int m = 0;
38851c0b2f7Stbbdev     CHECK_MESSAGE( ( limit.try_put( m++ )), "Newly constructed limiter node does not accept message." );
38949e08aacStbbdev     CHECK_MESSAGE( limit.decrementer().try_put( -threshold ), // close limiter's gate
39051c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message." );
39151c0b2f7Stbbdev     CHECK_MESSAGE( ( !limit.try_put( m++ )), "Closed limiter node's accepts message." );
39249e08aacStbbdev     CHECK_MESSAGE( limit.decrementer().try_put( threshold + 5 ),  // open limiter's gate
39351c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message." );
39451c0b2f7Stbbdev     for( int i = 0; i < threshold; ++i )
39551c0b2f7Stbbdev         CHECK_MESSAGE( ( limit.try_put( m++ )), "Limiter node does not accept message while open." );
39651c0b2f7Stbbdev     CHECK_MESSAGE( ( !limit.try_put( m )), "Limiter node's gate is not closed." );
39751c0b2f7Stbbdev     g.wait_for_all();
39851c0b2f7Stbbdev     int expected[] = {0, 2, 3, 4, 5, 6};
39951c0b2f7Stbbdev     int actual = -1; m = 0;
40051c0b2f7Stbbdev     while( queue.try_get(actual) )
40151c0b2f7Stbbdev         CHECK_MESSAGE( actual == expected[m++], "" );
40251c0b2f7Stbbdev     CHECK_MESSAGE( ( sizeof(expected) / sizeof(expected[0]) == m), "Not all messages have been processed." );
40351c0b2f7Stbbdev     g.wait_for_all();
40451c0b2f7Stbbdev 
40551c0b2f7Stbbdev     const size_t threshold2 = size_t(-1);
40651c0b2f7Stbbdev     tbb::flow::limiter_node<int, long long> limit2(g, threshold2);
40751c0b2f7Stbbdev     make_edge(limit2, queue);
40851c0b2f7Stbbdev     CHECK_MESSAGE( ( limit2.try_put( 1 )), "Newly constructed limiter node does not accept message." );
40951c0b2f7Stbbdev     long long decrement_value = (long long)( size_t(-1)/2 );
41049e08aacStbbdev     CHECK_MESSAGE( limit2.decrementer().try_put( -decrement_value ),
41151c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message" );
41251c0b2f7Stbbdev     CHECK_MESSAGE( ( limit2.try_put( 2 )), "Limiter's gate should not be closed yet." );
41349e08aacStbbdev     CHECK_MESSAGE( limit2.decrementer().try_put( -decrement_value ),
41451c0b2f7Stbbdev                    "Limiter node decrementer's port does not accept message" );
41551c0b2f7Stbbdev     CHECK_MESSAGE( ( !limit2.try_put( 3 )), "Overflow happened for internal counter." );
41651c0b2f7Stbbdev     int expected2[] = {1, 2};
41751c0b2f7Stbbdev     actual = -1; m = 0;
41851c0b2f7Stbbdev     while( queue.try_get(actual) )
41951c0b2f7Stbbdev         CHECK_MESSAGE( actual == expected2[m++], "" );
42051c0b2f7Stbbdev     CHECK_MESSAGE( ( sizeof(expected2) / sizeof(expected2[0]) == m), "Not all messages have been processed." );
42151c0b2f7Stbbdev     g.wait_for_all();
422*25e2b1ddSIlya Mishin 
423*25e2b1ddSIlya Mishin     const size_t threshold3 = 10;
424*25e2b1ddSIlya Mishin     tbb::flow::limiter_node<int, long long> limit3(g, threshold3);
425*25e2b1ddSIlya Mishin     make_edge(limit3, queue);
426*25e2b1ddSIlya Mishin     long long decrement_value3 = 3;
427*25e2b1ddSIlya Mishin     CHECK_MESSAGE( limit3.decrementer().try_put( -decrement_value3 ),
428*25e2b1ddSIlya Mishin                    "Limiter node decrementer's port does not accept message" );
429*25e2b1ddSIlya Mishin 
430*25e2b1ddSIlya Mishin     m = 0;
431*25e2b1ddSIlya Mishin     while( limit3.try_put( m ) ){ m++; };
432*25e2b1ddSIlya Mishin     CHECK_MESSAGE( m == threshold3 - decrement_value3, "Not all messages have been accepted." );
433*25e2b1ddSIlya Mishin 
434*25e2b1ddSIlya Mishin     actual = -1; m = 0;
435*25e2b1ddSIlya Mishin     while( queue.try_get(actual) ){
436*25e2b1ddSIlya Mishin         CHECK_MESSAGE( actual == m++, "Not all messages have been processed." );
437*25e2b1ddSIlya Mishin     }
438*25e2b1ddSIlya Mishin 
439*25e2b1ddSIlya Mishin     g.wait_for_all();
440*25e2b1ddSIlya Mishin     CHECK_MESSAGE( m == threshold3 - decrement_value3, "Not all messages have been processed." );
44151c0b2f7Stbbdev }
44251c0b2f7Stbbdev 
44351c0b2f7Stbbdev void test_try_put_without_successors() {
44451c0b2f7Stbbdev     tbb::flow::graph g;
44551c0b2f7Stbbdev     std::size_t try_put_num{3};
44651c0b2f7Stbbdev     tbb::flow::buffer_node<int> bn(g);
44751c0b2f7Stbbdev     tbb::flow::limiter_node<int> ln(g, try_put_num);
44851c0b2f7Stbbdev     tbb::flow::make_edge(bn, ln);
44951c0b2f7Stbbdev     std::size_t i = 1;
45051c0b2f7Stbbdev     for (; i <= try_put_num; i++)
45151c0b2f7Stbbdev         bn.try_put(i);
45251c0b2f7Stbbdev 
45351c0b2f7Stbbdev     std::atomic<std::size_t> counter{0};
45451c0b2f7Stbbdev     tbb::flow::function_node<int, int> fn(g, tbb::flow::unlimited,
45551c0b2f7Stbbdev         [&](int input) {
45651c0b2f7Stbbdev             counter += input;
45751c0b2f7Stbbdev             return int{};
45851c0b2f7Stbbdev         }
45951c0b2f7Stbbdev     );
46051c0b2f7Stbbdev     tbb::flow::make_edge(ln, fn);
46151c0b2f7Stbbdev     g.wait_for_all();
46251c0b2f7Stbbdev     CHECK((counter == i * try_put_num / 2));
46351c0b2f7Stbbdev 
46451c0b2f7Stbbdev     // Check the lost message
46551c0b2f7Stbbdev     tbb::flow::remove_edge(bn, ln);
46649e08aacStbbdev     ln.decrementer().try_put(tbb::flow::continue_msg());
46751c0b2f7Stbbdev     bn.try_put(try_put_num + 1);
46851c0b2f7Stbbdev     g.wait_for_all();
46951c0b2f7Stbbdev     CHECK((counter == i * try_put_num / 2));
47051c0b2f7Stbbdev 
47151c0b2f7Stbbdev }
47251c0b2f7Stbbdev 
47351c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
47451c0b2f7Stbbdev #include <array>
47551c0b2f7Stbbdev #include <vector>
47651c0b2f7Stbbdev void test_follows_and_precedes_api() {
47751c0b2f7Stbbdev     using msg_t = tbb::flow::continue_msg;
47851c0b2f7Stbbdev 
47951c0b2f7Stbbdev     std::array<msg_t, 3> messages_for_follows= { {msg_t(), msg_t(), msg_t()} };
48051c0b2f7Stbbdev     std::vector<msg_t> messages_for_precedes = {msg_t()};
48151c0b2f7Stbbdev 
48251c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
48351c0b2f7Stbbdev         <msg_t, tbb::flow::limiter_node<msg_t, msg_t>>(messages_for_follows, 1000);
48451c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
48551c0b2f7Stbbdev         <msg_t, tbb::flow::limiter_node<msg_t, msg_t>>(messages_for_precedes, 1000);
48651c0b2f7Stbbdev 
48751c0b2f7Stbbdev }
48851c0b2f7Stbbdev #endif
48951c0b2f7Stbbdev 
49051c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
49151c0b2f7Stbbdev void test_deduction_guides() {
49251c0b2f7Stbbdev     using namespace tbb::flow;
49351c0b2f7Stbbdev 
49451c0b2f7Stbbdev     graph g;
49551c0b2f7Stbbdev     broadcast_node<int> br(g);
49651c0b2f7Stbbdev     limiter_node<int> l0(g, 100);
49751c0b2f7Stbbdev 
49851c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
49951c0b2f7Stbbdev     limiter_node l1(follows(br), 100);
50051c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(l1), limiter_node<int>>);
50151c0b2f7Stbbdev 
50251c0b2f7Stbbdev     limiter_node l2(precedes(br), 100);
50351c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(l2), limiter_node<int>>);
50451c0b2f7Stbbdev #endif
50551c0b2f7Stbbdev 
50651c0b2f7Stbbdev     limiter_node l3(l0);
50751c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(l3), limiter_node<int>>);
50851c0b2f7Stbbdev }
50951c0b2f7Stbbdev #endif
51051c0b2f7Stbbdev 
51151c0b2f7Stbbdev //! Test puts on limiter_node with decrements and varying parallelism levels
51251c0b2f7Stbbdev //! \brief \ref error_guessing
51351c0b2f7Stbbdev TEST_CASE("Serial and parallel tests") {
51451c0b2f7Stbbdev     for (unsigned i = 1; i <= 2 * utils::MaxThread; ++i) {
51551c0b2f7Stbbdev         tbb::task_arena arena(i);
51651c0b2f7Stbbdev         arena.execute(
51751c0b2f7Stbbdev             [i]() {
51851c0b2f7Stbbdev                 test_serial<int>();
51951c0b2f7Stbbdev                 test_parallel<int>(i);
52051c0b2f7Stbbdev             }
52151c0b2f7Stbbdev         );
52251c0b2f7Stbbdev     }
52351c0b2f7Stbbdev }
52451c0b2f7Stbbdev 
52551c0b2f7Stbbdev //! Test initial put of continue_msg on decrementer port does not stop message flow
52651c0b2f7Stbbdev //! \brief \ref error_guessing
52751c0b2f7Stbbdev TEST_CASE("Test continue_msg reception") {
52851c0b2f7Stbbdev     test_continue_msg_reception();
52951c0b2f7Stbbdev }
53051c0b2f7Stbbdev 
53151c0b2f7Stbbdev //! Test multifunction_node connected to limiter_node
53251c0b2f7Stbbdev //! \brief \ref error_guessing
53351c0b2f7Stbbdev TEST_CASE("Multifunction connected to limiter") {
53451c0b2f7Stbbdev     test_multifunction_to_limiter(30,3);
53551c0b2f7Stbbdev     test_multifunction_to_limiter(300,13);
53651c0b2f7Stbbdev     test_multifunction_to_limiter(3000,1);
53751c0b2f7Stbbdev }
53851c0b2f7Stbbdev 
53951c0b2f7Stbbdev //! Test message release if successor doesn't accept
54051c0b2f7Stbbdev //! \brief \ref requirement
54151c0b2f7Stbbdev TEST_CASE("Message is released if successor does not accept") {
54251c0b2f7Stbbdev     test_reserve_release_messages();
54351c0b2f7Stbbdev }
54451c0b2f7Stbbdev 
54551c0b2f7Stbbdev //! Test decrementer
54651c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
54751c0b2f7Stbbdev TEST_CASE("Decrementer") {
54851c0b2f7Stbbdev     test_decrementer();
54951c0b2f7Stbbdev }
55051c0b2f7Stbbdev 
55151c0b2f7Stbbdev //! Test try_put() without successor
55251c0b2f7Stbbdev //! \brief \ref error_guessing
55351c0b2f7Stbbdev TEST_CASE("Test try_put() without successors") {
55451c0b2f7Stbbdev     test_try_put_without_successors();
55551c0b2f7Stbbdev }
55651c0b2f7Stbbdev 
55751c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
55851c0b2f7Stbbdev //! Test follows and precedes API
55951c0b2f7Stbbdev //! \brief \ref error_guessing
56051c0b2f7Stbbdev TEST_CASE( "Support for follows and precedes API" ) {
56151c0b2f7Stbbdev     test_follows_and_precedes_api();
56251c0b2f7Stbbdev }
56351c0b2f7Stbbdev #endif
56451c0b2f7Stbbdev 
56551c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
56651c0b2f7Stbbdev //! Test deduction guides
56751c0b2f7Stbbdev //! \brief \ref requirement
56851c0b2f7Stbbdev TEST_CASE( "Deduction guides" ) {
56951c0b2f7Stbbdev     test_deduction_guides();
57051c0b2f7Stbbdev }
57151c0b2f7Stbbdev #endif
572