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 17*64715f43SIlya Mishin #ifdef TBB_TEST_LOW_WORKLOAD 18*64715f43SIlya Mishin #undef MAX_TUPLE_TEST_SIZE 19*64715f43SIlya Mishin #define MAX_TUPLE_TEST_SIZE 3 20*64715f43SIlya Mishin #endif 21*64715f43SIlya Mishin 2251c0b2f7Stbbdev #include "common/config.h" 2351c0b2f7Stbbdev 2451c0b2f7Stbbdev #include "test_join_node.h" 2551c0b2f7Stbbdev 2651c0b2f7Stbbdev //! \file test_join_node.cpp 2751c0b2f7Stbbdev //! \brief Test for [flow_graph.join_node] specification 2851c0b2f7Stbbdev 2951c0b2f7Stbbdev 3051c0b2f7Stbbdev static std::atomic<int> output_count; 3151c0b2f7Stbbdev 3251c0b2f7Stbbdev // get the tag from the output tuple and emit it. 3351c0b2f7Stbbdev // the first tuple component is tag * 2 cast to the type 3451c0b2f7Stbbdev template<typename OutputTupleType> 3551c0b2f7Stbbdev class recirc_output_func_body { 3651c0b2f7Stbbdev public: 3751c0b2f7Stbbdev // we only need this to use input_node_helper 3851c0b2f7Stbbdev typedef typename tbb::flow::join_node<OutputTupleType, tbb::flow::tag_matching> join_node_type; 3951c0b2f7Stbbdev static const int N = std::tuple_size<OutputTupleType>::value; 4051c0b2f7Stbbdev int operator()(const OutputTupleType &v) { 4151c0b2f7Stbbdev int out = int(std::get<0>(v))/2; 4251c0b2f7Stbbdev input_node_helper<N, join_node_type>::only_check_value(out, v); 4351c0b2f7Stbbdev ++output_count; 4451c0b2f7Stbbdev return out; 4551c0b2f7Stbbdev } 4651c0b2f7Stbbdev }; 4751c0b2f7Stbbdev 4851c0b2f7Stbbdev template<typename JType> 4951c0b2f7Stbbdev class tag_recirculation_test { 5051c0b2f7Stbbdev public: 5151c0b2f7Stbbdev typedef typename JType::output_type TType; 5251c0b2f7Stbbdev typedef typename std::tuple<int, tbb::flow::continue_msg> input_tuple_type; 5351c0b2f7Stbbdev typedef tbb::flow::join_node<input_tuple_type, tbb::flow::reserving> input_join_type; 5451c0b2f7Stbbdev static const int N = std::tuple_size<TType>::value; 5551c0b2f7Stbbdev static void test() { 5651c0b2f7Stbbdev input_node_helper<N, JType>::print_remark("Recirculation test of tag-matching join"); 5751c0b2f7Stbbdev INFO(" >\n"); 5851c0b2f7Stbbdev for(int maxTag = 1; maxTag <10; maxTag *= 3) { 5951c0b2f7Stbbdev for(int i = 0; i < N; ++i) all_input_nodes[i][0] = NULL; 6051c0b2f7Stbbdev 6151c0b2f7Stbbdev tbb::flow::graph g; 6251c0b2f7Stbbdev // this is the tag-matching join we're testing 6351c0b2f7Stbbdev JType * my_join = makeJoin<N, JType, tbb::flow::tag_matching>::create(g); 6451c0b2f7Stbbdev // input_node for continue messages 6551c0b2f7Stbbdev tbb::flow::input_node<tbb::flow::continue_msg> snode(g, recirc_input_node_body()); 6651c0b2f7Stbbdev // reserving join that matches recirculating tags with continue messages. 6751c0b2f7Stbbdev input_join_type * my_input_join = makeJoin<2, input_join_type, tbb::flow::reserving>::create(g); 6851c0b2f7Stbbdev // tbb::flow::make_edge(snode, tbb::flow::input_port<1>(*my_input_join)); 6951c0b2f7Stbbdev tbb::flow::make_edge(snode, std::get<1>(my_input_join->input_ports())); 7051c0b2f7Stbbdev // queue to hold the tags 7151c0b2f7Stbbdev tbb::flow::queue_node<int> tag_queue(g); 7251c0b2f7Stbbdev tbb::flow::make_edge(tag_queue, tbb::flow::input_port<0>(*my_input_join)); 7351c0b2f7Stbbdev // add all the function_nodes that are inputs to the tag-matching join 7451c0b2f7Stbbdev input_node_helper<N, JType>::add_recirc_func_nodes(*my_join, *my_input_join, g); 7551c0b2f7Stbbdev // add the function_node that accepts the output of the join and emits the int tag it was based on 7651c0b2f7Stbbdev tbb::flow::function_node<TType, int> recreate_tag(g, tbb::flow::unlimited, recirc_output_func_body<TType>()); 7751c0b2f7Stbbdev tbb::flow::make_edge(*my_join, recreate_tag); 7851c0b2f7Stbbdev // now the recirculating part (output back to the queue) 7951c0b2f7Stbbdev tbb::flow::make_edge(recreate_tag, tag_queue); 8051c0b2f7Stbbdev 8151c0b2f7Stbbdev // put the tags into the queue 8251c0b2f7Stbbdev for(int t = 1; t<=maxTag; ++t) tag_queue.try_put(t); 8351c0b2f7Stbbdev 8451c0b2f7Stbbdev input_count = Recirc_count; 8551c0b2f7Stbbdev output_count = 0; 8651c0b2f7Stbbdev 8751c0b2f7Stbbdev // start up the source node to get things going 8851c0b2f7Stbbdev snode.activate(); 8951c0b2f7Stbbdev 9051c0b2f7Stbbdev // wait for everything to stop 9151c0b2f7Stbbdev g.wait_for_all(); 9251c0b2f7Stbbdev 9351c0b2f7Stbbdev CHECK_MESSAGE( (output_count==Recirc_count), "not all instances were received"); 9451c0b2f7Stbbdev 9551c0b2f7Stbbdev int j{}; 9651c0b2f7Stbbdev // grab the tags from the queue, record them 9751c0b2f7Stbbdev std::vector<bool> out_tally(maxTag, false); 9851c0b2f7Stbbdev for(int i = 0; i < maxTag; ++i) { 9951c0b2f7Stbbdev CHECK_MESSAGE( (tag_queue.try_get(j)), "not enough tags in queue"); 10051c0b2f7Stbbdev CHECK_MESSAGE( (!out_tally.at(j-1)), "duplicate tag from queue"); 10151c0b2f7Stbbdev out_tally[j-1] = true; 10251c0b2f7Stbbdev } 10351c0b2f7Stbbdev CHECK_MESSAGE( (!tag_queue.try_get(j)), "Extra tags in recirculation queue"); 10451c0b2f7Stbbdev 10551c0b2f7Stbbdev // deconstruct graph 10651c0b2f7Stbbdev input_node_helper<N, JType>::remove_recirc_func_nodes(*my_join, *my_input_join); 10751c0b2f7Stbbdev tbb::flow::remove_edge(*my_join, recreate_tag); 10851c0b2f7Stbbdev makeJoin<N, JType, tbb::flow::tag_matching>::destroy(my_join); 10951c0b2f7Stbbdev tbb::flow::remove_edge(tag_queue, tbb::flow::input_port<0>(*my_input_join)); 11051c0b2f7Stbbdev tbb::flow::remove_edge(snode, tbb::flow::input_port<1>(*my_input_join)); 11151c0b2f7Stbbdev makeJoin<2, input_join_type, tbb::flow::reserving>::destroy(my_input_join); 11251c0b2f7Stbbdev } 11351c0b2f7Stbbdev } 11451c0b2f7Stbbdev }; 11551c0b2f7Stbbdev 11651c0b2f7Stbbdev template<typename JType> 11751c0b2f7Stbbdev class generate_recirc_test { 11851c0b2f7Stbbdev public: 11951c0b2f7Stbbdev typedef tbb::flow::join_node<JType, tbb::flow::tag_matching> join_node_type; 12051c0b2f7Stbbdev static void do_test() { 12151c0b2f7Stbbdev tag_recirculation_test<join_node_type>::test(); 12251c0b2f7Stbbdev } 12351c0b2f7Stbbdev }; 12451c0b2f7Stbbdev 12551c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET 12651c0b2f7Stbbdev #include <array> 12751c0b2f7Stbbdev #include <vector> 12851c0b2f7Stbbdev void test_follows_and_precedes_api() { 12951c0b2f7Stbbdev using msg_t = tbb::flow::continue_msg; 13051c0b2f7Stbbdev using JoinOutputType = std::tuple<msg_t, msg_t, msg_t>; 13151c0b2f7Stbbdev 13251c0b2f7Stbbdev std::array<msg_t, 3> messages_for_follows = { {msg_t(), msg_t(), msg_t()} }; 13351c0b2f7Stbbdev std::vector<msg_t> messages_for_precedes = {msg_t(), msg_t(), msg_t()}; 13451c0b2f7Stbbdev 13551c0b2f7Stbbdev follows_and_precedes_testing::test_follows 13651c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType>, tbb::flow::buffer_node<msg_t>>(messages_for_follows); 13751c0b2f7Stbbdev follows_and_precedes_testing::test_follows 13851c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::queueing>>(messages_for_follows); 13951c0b2f7Stbbdev follows_and_precedes_testing::test_follows 14051c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::reserving>, tbb::flow::buffer_node<msg_t>>(messages_for_follows); 14151c0b2f7Stbbdev auto b = [](msg_t) { return msg_t(); }; 14251c0b2f7Stbbdev class hash_compare { 14351c0b2f7Stbbdev public: 14451c0b2f7Stbbdev std::size_t hash(msg_t) const { return 0; } 14551c0b2f7Stbbdev bool equal(msg_t, msg_t) const { return true; } 14651c0b2f7Stbbdev }; 14751c0b2f7Stbbdev follows_and_precedes_testing::test_follows 14851c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<msg_t, hash_compare>>, tbb::flow::buffer_node<msg_t>> 14951c0b2f7Stbbdev (messages_for_follows, b, b, b); 15051c0b2f7Stbbdev 15151c0b2f7Stbbdev follows_and_precedes_testing::test_precedes 15251c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType>>(messages_for_precedes); 15351c0b2f7Stbbdev follows_and_precedes_testing::test_precedes 15451c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::queueing>>(messages_for_precedes); 15551c0b2f7Stbbdev follows_and_precedes_testing::test_precedes 15651c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::reserving>>(messages_for_precedes); 15751c0b2f7Stbbdev follows_and_precedes_testing::test_precedes 15851c0b2f7Stbbdev <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<msg_t, hash_compare>>> 15951c0b2f7Stbbdev (messages_for_precedes, b, b, b); 16051c0b2f7Stbbdev } 16151c0b2f7Stbbdev #endif 16251c0b2f7Stbbdev 16351c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 16451c0b2f7Stbbdev void test_deduction_guides() { 16551c0b2f7Stbbdev using namespace tbb::flow; 16651c0b2f7Stbbdev 16751c0b2f7Stbbdev graph g; 16851c0b2f7Stbbdev using tuple_type = std::tuple<int, int, int>; 16951c0b2f7Stbbdev broadcast_node<int> b1(g), b2(g), b3(g); 17051c0b2f7Stbbdev broadcast_node<tuple_type> b4(g); 17151c0b2f7Stbbdev join_node<tuple_type> j0(g); 17251c0b2f7Stbbdev 17351c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET 17451c0b2f7Stbbdev join_node j1(follows(b1, b2, b3)); 17551c0b2f7Stbbdev static_assert(std::is_same_v<decltype(j1), join_node<tuple_type>>); 17651c0b2f7Stbbdev 17751c0b2f7Stbbdev join_node j2(follows(b1, b2, b3), reserving()); 17851c0b2f7Stbbdev static_assert(std::is_same_v<decltype(j2), join_node<tuple_type, reserving>>); 17951c0b2f7Stbbdev 18051c0b2f7Stbbdev join_node j3(precedes(b4)); 18151c0b2f7Stbbdev static_assert(std::is_same_v<decltype(j3), join_node<tuple_type>>); 18251c0b2f7Stbbdev 18351c0b2f7Stbbdev join_node j4(precedes(b4), reserving()); 18451c0b2f7Stbbdev static_assert(std::is_same_v<decltype(j4), join_node<tuple_type, reserving>>); 18551c0b2f7Stbbdev #endif 18651c0b2f7Stbbdev 18751c0b2f7Stbbdev join_node j5(j0); 18851c0b2f7Stbbdev static_assert(std::is_same_v<decltype(j5), join_node<tuple_type>>); 18951c0b2f7Stbbdev } 19051c0b2f7Stbbdev 19151c0b2f7Stbbdev #endif 19251c0b2f7Stbbdev 19351c0b2f7Stbbdev namespace multiple_predecessors { 19451c0b2f7Stbbdev 19551c0b2f7Stbbdev using namespace tbb::flow; 19651c0b2f7Stbbdev 19751c0b2f7Stbbdev using join_node_t = join_node<std::tuple<continue_msg, continue_msg, continue_msg>, reserving>; 19851c0b2f7Stbbdev using queue_node_t = queue_node<std::tuple<continue_msg, continue_msg, continue_msg>>; 19951c0b2f7Stbbdev 20051c0b2f7Stbbdev void twist_join_connections( 20151c0b2f7Stbbdev buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2, buffer_node<continue_msg>& bn3, 20251c0b2f7Stbbdev join_node_t& jn) 20351c0b2f7Stbbdev { 20451c0b2f7Stbbdev // order, in which edges are created/destroyed, is important 20551c0b2f7Stbbdev make_edge(bn1, input_port<0>(jn)); 20651c0b2f7Stbbdev make_edge(bn2, input_port<0>(jn)); 20751c0b2f7Stbbdev make_edge(bn3, input_port<0>(jn)); 20851c0b2f7Stbbdev 20951c0b2f7Stbbdev remove_edge(bn3, input_port<0>(jn)); 21051c0b2f7Stbbdev make_edge (bn3, input_port<2>(jn)); 21151c0b2f7Stbbdev 21251c0b2f7Stbbdev remove_edge(bn2, input_port<0>(jn)); 21351c0b2f7Stbbdev make_edge (bn2, input_port<1>(jn)); 21451c0b2f7Stbbdev } 21551c0b2f7Stbbdev 21651c0b2f7Stbbdev std::unique_ptr<join_node_t> connect_join_via_make_edge( 21751c0b2f7Stbbdev graph& g, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2, 21851c0b2f7Stbbdev buffer_node<continue_msg>& bn3, queue_node_t& qn) 21951c0b2f7Stbbdev { 22051c0b2f7Stbbdev std::unique_ptr<join_node_t> jn( new join_node_t(g) ); 22151c0b2f7Stbbdev twist_join_connections( bn1, bn2, bn3, *jn ); 22251c0b2f7Stbbdev make_edge(*jn, qn); 22351c0b2f7Stbbdev return jn; 22451c0b2f7Stbbdev } 22551c0b2f7Stbbdev 22651c0b2f7Stbbdev #if TBB_PREVIEW_FLOW_GRAPH_FEATURES 22751c0b2f7Stbbdev std::unique_ptr<join_node_t> connect_join_via_follows( 22851c0b2f7Stbbdev graph&, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2, 22951c0b2f7Stbbdev buffer_node<continue_msg>& bn3, queue_node_t& qn) 23051c0b2f7Stbbdev { 23151c0b2f7Stbbdev auto bn_set = make_node_set(bn1, bn2, bn3); 23251c0b2f7Stbbdev std::unique_ptr<join_node_t> jn( new join_node_t(follows(bn_set)) ); 23351c0b2f7Stbbdev make_edge(*jn, qn); 23451c0b2f7Stbbdev return jn; 23551c0b2f7Stbbdev } 23651c0b2f7Stbbdev 23751c0b2f7Stbbdev std::unique_ptr<join_node_t> connect_join_via_precedes( 23851c0b2f7Stbbdev graph&, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2, 23951c0b2f7Stbbdev buffer_node<continue_msg>& bn3, queue_node_t& qn) 24051c0b2f7Stbbdev { 24151c0b2f7Stbbdev auto qn_set = make_node_set(qn); 24251c0b2f7Stbbdev auto qn_copy_set = qn_set; 24351c0b2f7Stbbdev std::unique_ptr<join_node_t> jn( new join_node_t(precedes(qn_copy_set)) ); 24451c0b2f7Stbbdev twist_join_connections( bn1, bn2, bn3, *jn ); 24551c0b2f7Stbbdev return jn; 24651c0b2f7Stbbdev } 24751c0b2f7Stbbdev #endif // TBB_PREVIEW_FLOW_GRAPH_FEATURES 24851c0b2f7Stbbdev 24951c0b2f7Stbbdev void run_and_check( 25051c0b2f7Stbbdev graph& g, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2, 25151c0b2f7Stbbdev buffer_node<continue_msg>& bn3, queue_node_t& qn, bool expected) 25251c0b2f7Stbbdev { 25351c0b2f7Stbbdev std::tuple<continue_msg, continue_msg, continue_msg> msg; 25451c0b2f7Stbbdev 25551c0b2f7Stbbdev bn1.try_put(continue_msg()); 25651c0b2f7Stbbdev bn2.try_put(continue_msg()); 25751c0b2f7Stbbdev bn3.try_put(continue_msg()); 25851c0b2f7Stbbdev g.wait_for_all(); 25951c0b2f7Stbbdev 26051c0b2f7Stbbdev CHECK_MESSAGE( 26151c0b2f7Stbbdev (qn.try_get(msg) == expected), 26251c0b2f7Stbbdev "Unexpected message absence/existence at the end of the graph." 26351c0b2f7Stbbdev ); 26451c0b2f7Stbbdev } 26551c0b2f7Stbbdev 26651c0b2f7Stbbdev template<typename ConnectJoinNodeFunc> 26751c0b2f7Stbbdev void test(ConnectJoinNodeFunc&& connect_join_node) { 26851c0b2f7Stbbdev graph g; 26951c0b2f7Stbbdev buffer_node<continue_msg> bn1(g); 27051c0b2f7Stbbdev buffer_node<continue_msg> bn2(g); 27151c0b2f7Stbbdev buffer_node<continue_msg> bn3(g); 27251c0b2f7Stbbdev queue_node_t qn(g); 27351c0b2f7Stbbdev 27451c0b2f7Stbbdev auto jn = connect_join_node(g, bn1, bn2, bn3, qn); 27551c0b2f7Stbbdev 27651c0b2f7Stbbdev run_and_check(g, bn1, bn2, bn3, qn, /*expected=*/true); 27751c0b2f7Stbbdev 27851c0b2f7Stbbdev remove_edge(bn3, input_port<2>(*jn)); 27951c0b2f7Stbbdev remove_edge(bn2, input_port<1>(*jn)); 28051c0b2f7Stbbdev remove_edge(bn1, input_port<0>(*jn)); 28151c0b2f7Stbbdev remove_edge(*jn, qn); 28251c0b2f7Stbbdev 28351c0b2f7Stbbdev run_and_check(g, bn1, bn2, bn3, qn, /*expected=*/false); 28451c0b2f7Stbbdev } 28551c0b2f7Stbbdev } // namespace multiple_predecessors 28651c0b2f7Stbbdev 28751c0b2f7Stbbdev 28851c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET 28951c0b2f7Stbbdev //! Test follows and precedes API 29051c0b2f7Stbbdev //! \brief \ref error_guessing 29151c0b2f7Stbbdev TEST_CASE("Test follows and preceedes API"){ 29251c0b2f7Stbbdev test_follows_and_precedes_api(); 29351c0b2f7Stbbdev } 29451c0b2f7Stbbdev #endif 29551c0b2f7Stbbdev 29651c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 29751c0b2f7Stbbdev //! Test deduction guides 29851c0b2f7Stbbdev //! \brief \ref requirement 29951c0b2f7Stbbdev TEST_CASE("Deduction guides test"){ 30051c0b2f7Stbbdev test_deduction_guides(); 30151c0b2f7Stbbdev } 30251c0b2f7Stbbdev #endif 30351c0b2f7Stbbdev 30451c0b2f7Stbbdev //! Test hash buffers behavior 30551c0b2f7Stbbdev //! \brief \ref error_guessing 30651c0b2f7Stbbdev TEST_CASE("Tagged buffers test"){ 30751c0b2f7Stbbdev TestTaggedBuffers(); 30851c0b2f7Stbbdev } 30951c0b2f7Stbbdev 31051c0b2f7Stbbdev //! Test with various policies and tuple sizes 31151c0b2f7Stbbdev //! \brief \ref error_guessing 31251c0b2f7Stbbdev TEST_CASE("Main test"){ 31351c0b2f7Stbbdev test_main<tbb::flow::queueing>(); 31451c0b2f7Stbbdev test_main<tbb::flow::reserving>(); 31551c0b2f7Stbbdev test_main<tbb::flow::tag_matching>(); 31651c0b2f7Stbbdev } 31751c0b2f7Stbbdev 31851c0b2f7Stbbdev //! Test with recirculating tags 31951c0b2f7Stbbdev //! \brief \ref error_guessing 32051c0b2f7Stbbdev TEST_CASE("Recirculation test"){ 32151c0b2f7Stbbdev generate_recirc_test<std::tuple<int,float> >::do_test(); 32251c0b2f7Stbbdev } 32351c0b2f7Stbbdev 32451c0b2f7Stbbdev //! Test maintaining correct count of ports without input 32551c0b2f7Stbbdev //! \brief \ref error_guessing 32651c0b2f7Stbbdev TEST_CASE("Test removal of the predecessor while having none") { 32751c0b2f7Stbbdev using namespace multiple_predecessors; 32851c0b2f7Stbbdev 32951c0b2f7Stbbdev test(connect_join_via_make_edge); 33051c0b2f7Stbbdev #if TBB_PREVIEW_FLOW_GRAPH_FEATURES 33151c0b2f7Stbbdev test(connect_join_via_follows); 33251c0b2f7Stbbdev test(connect_join_via_precedes); 33351c0b2f7Stbbdev #endif 33451c0b2f7Stbbdev } 335