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