1 /* 2 Copyright (c) 2019-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 #ifndef __TBB_test_common_test_follows_and_precedes_api_H_ 18 #define __TBB_test_common_test_follows_and_precedes_api_H_ 19 20 #include "config.h" 21 #include "oneapi/tbb/flow_graph.h" 22 23 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET 24 #include <array> 25 #include <vector> 26 #include <type_traits> 27 28 namespace follows_and_precedes_testing{ 29 30 template <typename NodeType> 31 struct testing_method_follows: std::integral_constant<int, 0> {}; 32 33 template <typename... Args> 34 struct testing_method_follows<tbb::flow::join_node<Args...>> : std::integral_constant<int, 1> {}; 35 template <typename... Args> 36 struct testing_method_follows<tbb::flow::continue_node<Args...>> : std::integral_constant<int, 1> {}; 37 38 template <typename... Args> 39 struct testing_method_follows<tbb::flow::overwrite_node<Args...>> : std::integral_constant<int, 2> {}; 40 template <typename... Args> 41 struct testing_method_follows<tbb::flow::write_once_node<Args...>> : std::integral_constant<int, 2> {}; 42 43 template <typename... Args> 44 struct testing_method_follows<tbb::flow::multifunction_node<Args...>> : std::integral_constant<int, 3> {}; 45 46 template <typename MessageType, typename NodeType, typename PredecessorType> 47 class edge_checker_follows { 48 public: 49 static void check(tbb::flow::graph& g, 50 NodeType& node, 51 std::array<PredecessorType, 3>& preds, 52 std::array<MessageType, 3>& messages) { 53 check_impl(g, node, preds, messages, typename testing_method_follows<NodeType>::type()); 54 } 55 private: 56 // Test is applicable for: function_node, buffer_node, queue_node, priority_queue_node, limiter_node, 57 // broadcast_node, sequencer_node 58 static void check_impl(tbb::flow::graph& g, 59 NodeType& node, 60 std::array<PredecessorType, 3>& preds, 61 std::array<MessageType, 3>& messages, 62 std::integral_constant<int, 0>) { 63 tbb::flow::buffer_node<typename NodeType::output_type> buffer(g); 64 tbb::flow::make_edge(node, buffer); 65 66 for(size_t i = 0; i < 3; ++i) { 67 preds[i].try_put(messages[i]); 68 g.wait_for_all(); 69 70 typename NodeType::output_type storage; 71 CHECK_MESSAGE((buffer.try_get(storage) && !buffer.try_get(storage)), "Not exact edge quantity was made"); 72 } 73 } 74 // Test is applicable for: continue_node, join_node 75 static void check_impl(tbb::flow::graph& g, 76 NodeType& node, 77 std::array<PredecessorType, 3>& preds, 78 std::array<MessageType, 3>& messages, 79 std::integral_constant<int, 1>) { 80 tbb::flow::buffer_node<typename NodeType::output_type> buffer(g); 81 tbb::flow::make_edge(node, buffer); 82 83 for(size_t i = 0; i < 3; ++i) { 84 preds[i].try_put(messages[i]); 85 g.wait_for_all(); 86 } 87 88 typename NodeType::output_type storage; 89 CHECK_MESSAGE((buffer.try_get(storage) && !buffer.try_get(storage)), "Not exact edge quantity was made"); 90 } 91 // Test is applicable for: overwrite_node, write_once_node 92 static void check_impl(tbb::flow::graph& g, 93 NodeType& node, 94 std::array<PredecessorType, 3>& preds, 95 std::array<MessageType, 3>& messages, 96 std::integral_constant<int, 2>) { 97 for(size_t i = 0; i < 3; ++i) { 98 node.clear(); 99 preds[i].try_put(messages[i]); 100 g.wait_for_all(); 101 102 typename NodeType::output_type storage; 103 CHECK_MESSAGE((node.try_get(storage)), "Not exact edge quantity was made"); 104 } 105 } 106 // Test is applicable for: multifunction_node 107 static void check_impl(tbb::flow::graph& g, 108 NodeType& node, 109 std::array<PredecessorType, 3>& preds, 110 std::array<MessageType, 3>& messages, 111 std::integral_constant<int, 3>) { 112 typedef typename std::remove_reference<decltype(tbb::flow::output_port<0>(node))>::type multioutput; 113 tbb::flow::buffer_node<typename multioutput::output_type> buf(tbb::flow::follows(tbb::flow::output_port<0>(node))); 114 115 for(size_t i = 0; i < 3; ++i) { 116 preds[i].try_put(messages[i]); 117 g.wait_for_all(); 118 119 typename multioutput::output_type storage; 120 CHECK_MESSAGE((buf.try_get(storage) && ! buf.try_get(storage)), "Not exact edge quantity was made"); 121 } 122 } 123 }; 124 125 template <typename NodeType> 126 struct testing_method_precedes: std::integral_constant<int, 0> {}; 127 128 template <typename... Args> 129 struct testing_method_precedes<tbb::flow::buffer_node<Args...>> : std::integral_constant<int, 1> {}; 130 template <typename... Args> 131 struct testing_method_precedes<tbb::flow::queue_node<Args...>> : std::integral_constant<int, 1> {}; 132 template <typename... Args> 133 struct testing_method_precedes<tbb::flow::priority_queue_node<Args...>> : std::integral_constant<int, 1> {}; 134 template <typename... Args> 135 struct testing_method_precedes<tbb::flow::sequencer_node<Args...>> : std::integral_constant<int, 1> {}; 136 137 template <typename... Args> 138 struct testing_method_precedes<tbb::flow::join_node<Args...>> : std::integral_constant<int, 2> {}; 139 140 template <typename MessageType, typename NodeType> 141 class edge_checker_precedes { 142 using NodeOutputType = typename NodeType::output_type; 143 using SuccessorOutputType = typename tbb::flow::buffer_node<NodeOutputType>::output_type; 144 public: 145 static void check(tbb::flow::graph& g, 146 NodeType& node, 147 std::array<tbb::flow::buffer_node<NodeOutputType>, 3>& successors, 148 std::vector<MessageType>& messages) { 149 check_impl(g, node, successors, messages, typename testing_method_precedes<NodeType>::type()); 150 } 151 private: 152 // Testing is applicable for: continue_node, function_node, overwrite_node, buffer_node, 153 // broadcast_node, write_once_node, limiter_node 154 static void check_impl(tbb::flow::graph& g, 155 NodeType& node, 156 std::array<tbb::flow::buffer_node<NodeOutputType>, 3>& successors, 157 std::vector<MessageType>& messages, 158 std::integral_constant<int, 0>) { 159 CHECK_MESSAGE((messages.size() == 1), 160 "messages.size() has to be 1 to test nodes what broadcast message to all the successors"); 161 162 node.try_put(messages[0]); 163 g.wait_for_all(); 164 165 SuccessorOutputType storage; 166 for(auto& successor: successors) { 167 CHECK_MESSAGE((successor.try_get(storage) && !successor.try_get(storage)), 168 "Not exact edge quantity was made"); 169 } 170 } 171 // Testing is applicable for: buffer_node, queue_node, priority_queue_node, sequencer_node 172 static void check_impl(tbb::flow::graph& g, 173 NodeType& node, 174 std::array<tbb::flow::buffer_node<typename NodeType::output_type>, 3>& successors, 175 std::vector<MessageType>& messages, 176 std::integral_constant<int, 1>) { 177 CHECK_MESSAGE((messages.size() == 3), 178 "messages.size() has to be 3 to test nodes what send a message to the first available successor"); 179 180 tbb::flow::write_once_node<SuccessorOutputType> 181 buffer(tbb::flow::follows(successors[0], successors[1], successors[2])); 182 183 for(size_t i = 0; i < 3; ++i) { 184 node.try_put(messages[i]); 185 g.wait_for_all(); 186 187 SuccessorOutputType storage; 188 CHECK_MESSAGE((buffer.try_get(storage)), "Not exact edge quantity was made"); 189 190 buffer.clear(); 191 } 192 } 193 // Testing is applicable for: join_node 194 static void check_impl(tbb::flow::graph& g, 195 NodeType& node, 196 std::array<tbb::flow::buffer_node<typename NodeType::output_type>, 3>& successors, 197 std::vector<MessageType>& messages, 198 std::integral_constant<int, 2>) { 199 CHECK_MESSAGE((messages.size() == 3), 200 "messages.size() has to be 3 to test nodes what send a message to the first available successor"); 201 std::array<tbb::flow::buffer_node<MessageType>, 3> preds = { 202 { 203 tbb::flow::buffer_node<MessageType>(g), 204 tbb::flow::buffer_node<MessageType>(g), 205 tbb::flow::buffer_node<MessageType>(g) 206 } 207 }; 208 209 make_edge(preds[0], tbb::flow::input_port<0>(node)); 210 make_edge(preds[1], tbb::flow::input_port<1>(node)); 211 make_edge(preds[2], tbb::flow::input_port<2>(node)); 212 213 for(size_t i = 0; i < 3; ++i) { 214 preds[i].try_put(messages[i]); 215 g.wait_for_all(); 216 } 217 218 typename NodeType::output_type storage; 219 for(auto& successor: successors) { 220 CHECK_MESSAGE((successor.try_get(storage) && !successor.try_get(storage)), 221 "Not exact edge quantity was made"); 222 } 223 } 224 }; 225 226 template<typename MessageType, 227 typename NodeType, 228 typename PredecessorType=tbb::flow::broadcast_node<MessageType>, 229 typename... ConstructorArgs> 230 void test_follows(std::array<MessageType, 3>& messages, ConstructorArgs&&... args) { 231 using namespace tbb::flow; 232 233 graph g; 234 std::array<PredecessorType, 3> preds = { 235 { 236 PredecessorType(g), 237 PredecessorType(g), 238 PredecessorType(g) 239 } 240 }; 241 242 NodeType node(follows(preds[0], preds[1], preds[2]), std::forward<ConstructorArgs>(args)...); 243 244 edge_checker_follows<MessageType, NodeType, PredecessorType>::check(g, node, preds, messages); 245 } 246 247 template<typename MessageType, 248 typename NodeType, 249 typename... ConstructorArgs> 250 void test_precedes(std::vector<MessageType>& messages, ConstructorArgs&&... args) { 251 using namespace tbb::flow; 252 253 using SuccessorType = buffer_node<typename NodeType::output_type>; 254 255 graph g; 256 257 std::array<SuccessorType, 3> successors = { {SuccessorType(g), SuccessorType(g), SuccessorType(g)} }; 258 259 NodeType node(precedes(successors[0], successors[1], successors[2]), std::forward<ConstructorArgs>(args)...); 260 261 edge_checker_precedes<MessageType, NodeType>::check(g, node, successors, messages); 262 } 263 264 } // follows_and_precedes_testing 265 #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET 266 #endif // __TBB_test_common_test_follows_and_precedes_api_H_ 267