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