1 /* 2 Copyright (c) 2020-2021 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #if __INTEL_COMPILER && _MSC_VER 18 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated 19 #endif 20 21 #include "common/test.h" 22 23 #include "common/utils.h" 24 #include "common/graph_utils.h" 25 26 #include "oneapi/tbb/flow_graph.h" 27 #include "oneapi/tbb/task_arena.h" 28 29 #include "oneapi/tbb/global_control.h" 30 #include "conformance_flowgraph.h" 31 32 //! \file conformance_continue_node.cpp 33 //! \brief Test for [flow_graph.continue_node] specification 34 35 /* 36 TODO: implement missing conformance tests for continue_node: 37 - [ ] For `test_forwarding' check that the value passed is the actual one received. 38 - [ ] The `copy_body' function copies altered body (e.g. after its successful invocation). 39 - [ ] Improve CTAD test. 40 - [ ] Improve constructors test, including addition of calls to constructors with 41 `number_of_predecessors' parameter. 42 - [ ] Explicit test for copy constructor of the node. 43 - [ ] Rewrite test_priority. 44 - [ ] Check `Output' type indeed copy-constructed and copy-assigned while working with the node. 45 - [ ] Explicit test for correct working of `number_of_predecessors' constructor parameter, 46 including taking it into account when making and removing edges. 47 - [ ] Add testing of `try_put' statement. In particular that it does not wait for the execution of 48 the body to complete. 49 */ 50 51 void test_cont_body(){ 52 oneapi::tbb::flow::graph g; 53 inc_functor<int> cf; 54 cf.execute_count = 0; 55 56 oneapi::tbb::flow::continue_node<int> node1(g, cf); 57 58 const size_t n = 10; 59 for(size_t i = 0; i < n; ++i) { 60 CHECK_MESSAGE((node1.try_put(oneapi::tbb::flow::continue_msg()) == true), 61 "continue_node::try_put() should never reject a message."); 62 } 63 g.wait_for_all(); 64 65 CHECK_MESSAGE( (cf.execute_count == n), "Body of the first node needs to be executed N times"); 66 } 67 68 template<typename O> 69 void test_inheritance(){ 70 using namespace oneapi::tbb::flow; 71 72 CHECK_MESSAGE( (std::is_base_of<graph_node, continue_node<O>>::value), "continue_node should be derived from graph_node"); 73 CHECK_MESSAGE( (std::is_base_of<receiver<continue_msg>, continue_node<O>>::value), "continue_node should be derived from receiver<Input>"); 74 CHECK_MESSAGE( (std::is_base_of<sender<O>, continue_node<O>>::value), "continue_node should be derived from sender<Output>"); 75 } 76 77 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 78 void test_deduction_guides(){ 79 oneapi::tbb::flow::graph g; 80 inc_functor<int> fun; 81 oneapi::tbb::flow::continue_node node1(g, fun); 82 } 83 #endif 84 85 void test_forwarding(){ 86 oneapi::tbb::flow::graph g; 87 inc_functor<int> fun; 88 fun.execute_count = 0; 89 90 oneapi::tbb::flow::continue_node<int> node1(g, fun); 91 test_push_receiver<int> node2(g); 92 test_push_receiver<int> node3(g); 93 94 oneapi::tbb::flow::make_edge(node1, node2); 95 oneapi::tbb::flow::make_edge(node1, node3); 96 97 node1.try_put(oneapi::tbb::flow::continue_msg()); 98 g.wait_for_all(); 99 100 CHECK_MESSAGE( (get_count(node2) == 1), "Descendant of the node must receive one message."); 101 CHECK_MESSAGE( (get_count(node3) == 1), "Descendant of the node must receive one message."); 102 } 103 104 void test_buffering(){ 105 oneapi::tbb::flow::graph g; 106 inc_functor<int> fun; 107 108 oneapi::tbb::flow::continue_node<int> node(g, fun); 109 oneapi::tbb::flow::limiter_node<int> rejecter(g, 0); 110 111 oneapi::tbb::flow::make_edge(node, rejecter); 112 node.try_put(oneapi::tbb::flow::continue_msg()); 113 114 int tmp = -1; 115 CHECK_MESSAGE( (node.try_get(tmp) == false), "try_get after rejection should not succeed"); 116 CHECK_MESSAGE( (tmp == -1), "try_get after rejection should not alter passed value"); 117 g.wait_for_all(); 118 } 119 120 void test_policy_ctors(){ 121 using namespace oneapi::tbb::flow; 122 graph g; 123 124 inc_functor<int> fun; 125 126 continue_node<int, lightweight> lw_node(g, fun); 127 } 128 129 void test_ctors(){ 130 using namespace oneapi::tbb::flow; 131 graph g; 132 133 inc_functor<int> fun; 134 135 continue_node<int> proto1(g, 2, fun, oneapi::tbb::flow::node_priority_t(1)); 136 } 137 138 template<typename O> 139 struct CopyCounterBody{ 140 size_t copy_count; 141 142 CopyCounterBody(): 143 copy_count(0) {} 144 145 CopyCounterBody(const CopyCounterBody<O>& other): 146 copy_count(other.copy_count + 1) {} 147 148 CopyCounterBody& operator=(const CopyCounterBody<O>& other){ 149 copy_count = other.copy_count + 1; 150 return *this; 151 } 152 153 O operator()(oneapi::tbb::flow::continue_msg){ 154 return 1; 155 } 156 }; 157 158 void test_copies(){ 159 using namespace oneapi::tbb::flow; 160 161 CopyCounterBody<int> b; 162 163 graph g; 164 continue_node<int> fn(g, b); 165 166 CopyCounterBody<int> b2 = copy_body<CopyCounterBody<int>, 167 continue_node<int>>(fn); 168 169 CHECK_MESSAGE( (b.copy_count + 2 <= b2.copy_count), "copy_body and constructor should copy bodies"); 170 } 171 172 173 void test_priority(){ 174 size_t concurrency_limit = 1; 175 oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_limit); 176 177 oneapi::tbb::flow::graph g; 178 179 oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg> source(g, 180 [](oneapi::tbb::flow::continue_msg){ return oneapi::tbb::flow::continue_msg();}); 181 source.try_put(oneapi::tbb::flow::continue_msg()); 182 183 first_functor<int>::first_id = -1; 184 first_functor<int> low_functor(1); 185 first_functor<int> high_functor(2); 186 187 oneapi::tbb::flow::continue_node<int, int> high(g, high_functor, oneapi::tbb::flow::node_priority_t(1)); 188 oneapi::tbb::flow::continue_node<int, int> low(g, low_functor); 189 190 make_edge(source, low); 191 make_edge(source, high); 192 193 g.wait_for_all(); 194 195 CHECK_MESSAGE( (first_functor<int>::first_id == 2), "High priority node should execute first"); 196 } 197 198 //! Test node costructors 199 //! \brief \ref requirement 200 TEST_CASE("continue_node constructors"){ 201 test_ctors(); 202 } 203 204 //! Test priorities work in single-threaded configuration 205 //! \brief \ref requirement 206 TEST_CASE("continue_node priority support"){ 207 test_priority(); 208 } 209 210 //! Test body copying and copy_body logic 211 //! \brief \ref interface 212 TEST_CASE("continue_node and body copying"){ 213 test_copies(); 214 } 215 216 //! Test constructors 217 //! \brief \ref interface 218 TEST_CASE("continue_node constructors"){ 219 test_policy_ctors(); 220 } 221 222 //! Test continue_node buffering 223 //! \brief \ref requirement 224 TEST_CASE("continue_node buffering"){ 225 test_buffering(); 226 } 227 228 //! Test function_node broadcasting 229 //! \brief \ref requirement 230 TEST_CASE("continue_node broadcast"){ 231 test_forwarding(); 232 } 233 234 //! Test deduction guides 235 //! \brief \ref interface \ref requirement 236 TEST_CASE("Deduction guides"){ 237 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 238 test_deduction_guides(); 239 #endif 240 } 241 242 //! Test inheritance relations 243 //! \brief \ref interface 244 TEST_CASE("continue_node superclasses"){ 245 test_inheritance<int>(); 246 test_inheritance<void*>(); 247 } 248 249 //! Test body execution 250 //! \brief \ref interface \ref requirement 251 TEST_CASE("continue body") { 252 test_cont_body(); 253 } 254