1 | /* |
2 | Copyright (c) 2005-2019 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 __TBB_CPF_BUILD |
18 | #define TBB_DEPRECATED_FLOW_NODE_EXTRACTION 1 |
19 | #endif |
20 | |
21 | #include "harness_graph.h" |
22 | #include "tbb/flow_graph.h" |
23 | |
24 | // |
25 | // Tests |
26 | // |
27 | |
28 | #if defined(_MSC_VER) && _MSC_VER < 1600 |
29 | #pragma warning (disable : 4503) //disabling the "decorated name length exceeded" warning for VS2008 and earlier |
30 | #endif |
31 | |
32 | #if TBB_DEPRECATED_FLOW_NODE_EXTRACTION |
33 | template< typename T > |
34 | class test_indexer_extract { |
35 | protected: |
36 | typedef tbb::flow::indexer_node<T, T> my_node_t; |
37 | typedef tbb::flow::queue_node<T> in_node_t; |
38 | typedef tbb::flow::queue_node<typename my_node_t::output_type> out_node_t; |
39 | |
40 | tbb::flow::graph g; |
41 | in_node_t in0; |
42 | in_node_t in1; |
43 | in_node_t in2; |
44 | my_node_t middle; |
45 | out_node_t out0; |
46 | out_node_t out1; |
47 | in_node_t *ins[3]; |
48 | out_node_t *outs[2]; |
49 | typename in_node_t::successor_type *ms_p0_ptr; |
50 | typename in_node_t::successor_type *ms_p1_ptr; |
51 | typename out_node_t::predecessor_type *mp_ptr; |
52 | typename in_node_t::predecessor_list_type in0_p_list; |
53 | typename in_node_t::successor_list_type in0_s_list; |
54 | typename in_node_t::predecessor_list_type in1_p_list; |
55 | typename in_node_t::successor_list_type in1_s_list; |
56 | typename in_node_t::predecessor_list_type in2_p_list; |
57 | typename in_node_t::successor_list_type in2_s_list; |
58 | typename out_node_t::predecessor_list_type out0_p_list; |
59 | typename out_node_t::successor_list_type out0_s_list; |
60 | typename out_node_t::predecessor_list_type out1_p_list; |
61 | typename out_node_t::successor_list_type out1_s_list; |
62 | typename in_node_t::predecessor_list_type mp0_list; |
63 | typename in_node_t::predecessor_list_type mp1_list; |
64 | typename out_node_t::successor_list_type ms_list; |
65 | |
66 | virtual void set_up_lists() { |
67 | in0_p_list.clear(); |
68 | in0_s_list.clear(); |
69 | in1_p_list.clear(); |
70 | in1_s_list.clear(); |
71 | in2_p_list.clear(); |
72 | in2_s_list.clear(); |
73 | out0_p_list.clear(); |
74 | out0_s_list.clear(); |
75 | out1_p_list.clear(); |
76 | out1_s_list.clear(); |
77 | mp0_list.clear(); |
78 | mp1_list.clear(); |
79 | ms_list.clear(); |
80 | |
81 | in0.copy_predecessors(in0_p_list); |
82 | in0.copy_successors(in0_s_list); |
83 | in1.copy_predecessors(in1_p_list); |
84 | in1.copy_successors(in1_s_list); |
85 | in2.copy_predecessors(in2_p_list); |
86 | in2.copy_successors(in2_s_list); |
87 | tbb::flow::input_port<0>(middle).copy_predecessors(mp0_list); |
88 | tbb::flow::input_port<1>(middle).copy_predecessors(mp1_list); |
89 | middle.copy_successors(ms_list); |
90 | out0.copy_predecessors(out0_p_list); |
91 | out0.copy_successors(out0_s_list); |
92 | out1.copy_predecessors(out1_p_list); |
93 | out1.copy_successors(out1_s_list); |
94 | } |
95 | |
96 | void check_output(int &r, typename my_node_t::output_type &v) { |
97 | T t = tbb::flow::cast_to<T>(v); |
98 | if ( t == 1 || t == 2 ) { |
99 | ASSERT( v.tag() == 0, "value came in on wrong port" ); |
100 | } else if ( t == 4 || t == 8 ) { |
101 | ASSERT( v.tag() == 1, "value came in on wrong port" ); |
102 | } else { |
103 | ASSERT( false, "incorrect value passed through indexer_node" ); |
104 | } |
105 | ASSERT( (r&t) == 0, "duplicate value passed through indexer_node" ); |
106 | r |= t; |
107 | } |
108 | |
109 | void make_and_validate_full_graph() { |
110 | /* in0 */ |
111 | /* \ */ |
112 | /* port0 out0 */ |
113 | /* / | / */ |
114 | /* in1 middle */ |
115 | /* | \ */ |
116 | /* in2 - port1 out1 */ |
117 | tbb::flow::make_edge( in0, tbb::flow::input_port<0>(middle) ); |
118 | tbb::flow::make_edge( in1, tbb::flow::input_port<0>(middle) ); |
119 | tbb::flow::make_edge( in2, tbb::flow::input_port<1>(middle) ); |
120 | tbb::flow::make_edge( middle, out0 ); |
121 | tbb::flow::make_edge( middle, out1 ); |
122 | |
123 | set_up_lists(); |
124 | |
125 | ASSERT( in0.predecessor_count() == 0 && in0_p_list.size() == 0, "expected 0 predecessors" ); |
126 | ASSERT( in0.successor_count() == 1 && in0_s_list.size() == 1 && *(in0_s_list.begin()) == ms_p0_ptr, "expected 1 successor" ); |
127 | ASSERT( in1.predecessor_count() == 0 && in1_p_list.size() == 0, "expected 0 predecessors" ); |
128 | ASSERT( in1.successor_count() == 1 && in1_s_list.size() == 1 && *(in1_s_list.begin()) == ms_p0_ptr, "expected 1 successor" ); |
129 | ASSERT( in2.predecessor_count() == 0 && in2_p_list.size() == 0, "expected 0 predecessors" ); |
130 | ASSERT( in2.successor_count() == 1 && in2_s_list.size() == 1 && *(in2_s_list.begin()) == ms_p1_ptr, "expected 1 successor" ); |
131 | ASSERT( tbb::flow::input_port<0>(middle).predecessor_count() == 2 && mp0_list.size() == 2, "expected 2 predecessors" ); |
132 | ASSERT( tbb::flow::input_port<1>(middle).predecessor_count() == 1 && mp1_list.size() == 1, "expected 1 predecessors" ); |
133 | ASSERT( middle.successor_count() == 2 && ms_list.size() == 2, "expected 2 successors" ); |
134 | ASSERT( out0.predecessor_count() == 1 && out0_p_list.size() == 1 && *(out0_p_list.begin()) == mp_ptr, "expected 1 predecessor" ); |
135 | ASSERT( out0.successor_count() == 0 && out0_s_list.size() == 0, "expected 0 successors" ); |
136 | ASSERT( out1.predecessor_count() == 1 && out1_p_list.size() == 1 && *(out1_p_list.begin()) == mp_ptr, "expected 1 predecessor" ); |
137 | ASSERT( out1.successor_count() == 0 && out1_s_list.size() == 0, "expected 0 successors" ); |
138 | |
139 | int first_pred = *(mp0_list.begin()) == ins[0] ? 0 : ( *(mp0_list.begin()) == ins[1] ? 1 : -1 ); |
140 | typename in_node_t::predecessor_list_type::iterator piv = mp0_list.begin();++piv; |
141 | int second_pred = *piv == ins[0] ? 0 : ( *piv == ins[1] ? 1 : -1 ); |
142 | ASSERT( first_pred != -1 && second_pred != -1 && first_pred != second_pred, "bad predecessor(s) for middle port 0" ); |
143 | |
144 | ASSERT( *(mp1_list.begin()) == ins[2], "bad predecessor for middle port 1" ); |
145 | |
146 | int first_succ = *(ms_list.begin()) == outs[0] ? 0 : ( *(ms_list.begin()) == outs[1] ? 1 : -1 ); |
147 | typename out_node_t::successor_list_type::iterator ms_vec_iter = ms_list.begin(); ++ms_vec_iter; |
148 | int second_succ = *ms_vec_iter == outs[0] ? 0 : ( *ms_vec_iter == outs[1] ? 1 : -1 ); |
149 | ASSERT( first_succ != -1 && second_succ != -1 && first_succ != second_succ, "bad successor(s) for middle" ); |
150 | |
151 | in0.try_put(1); |
152 | in1.try_put(2); |
153 | in2.try_put(8); |
154 | in2.try_put(4); |
155 | g.wait_for_all(); |
156 | |
157 | T v_in; |
158 | |
159 | ASSERT( in0.try_get(v_in) == false, "buffer should not have a value" ); |
160 | ASSERT( in1.try_get(v_in) == false, "buffer should not have a value" ); |
161 | ASSERT( in1.try_get(v_in) == false, "buffer should not have a value" ); |
162 | ASSERT( in2.try_get(v_in) == false, "buffer should not have a value" ); |
163 | ASSERT( in2.try_get(v_in) == false, "buffer should not have a value" ); |
164 | |
165 | typename my_node_t::output_type v; |
166 | T r = 0; |
167 | while ( out0.try_get(v) ) { |
168 | check_output(r,v); |
169 | g.wait_for_all(); |
170 | } |
171 | ASSERT( r == 15, "not all values received" ); |
172 | |
173 | r = 0; |
174 | while ( out1.try_get(v) ) { |
175 | check_output(r,v); |
176 | g.wait_for_all(); |
177 | } |
178 | ASSERT( r == 15, "not all values received" ); |
179 | g.wait_for_all(); |
180 | } |
181 | |
182 | void validate_partial_graph() { |
183 | /* in0 */ |
184 | /* */ |
185 | /* port0 out0 */ |
186 | /* / | */ |
187 | /* in1 middle */ |
188 | /* | \ */ |
189 | /* in2 - port1 out1 */ |
190 | set_up_lists(); |
191 | |
192 | ASSERT( in0.predecessor_count() == 0 && in0_p_list.size() == 0, "expected 0 predecessors" ); |
193 | ASSERT( in0.successor_count() == 0 && in0_s_list.size() == 0, "expected 0 successors" ); |
194 | ASSERT( in1.predecessor_count() == 0 && in1_p_list.size() == 0, "expected 0 predecessors" ); |
195 | ASSERT( in1.successor_count() == 1 && in1_s_list.size() == 1 && *(in1_s_list.begin()) == ms_p0_ptr, "expected 1 successor" ); |
196 | ASSERT( in2.predecessor_count() == 0 && in2_p_list.size() == 0, "expected 0 predecessors" ); |
197 | ASSERT( in2.successor_count() == 1 && in2_s_list.size() == 1 && *(in2_s_list.begin()) == ms_p1_ptr, "expected 1 successor" ); |
198 | ASSERT( tbb::flow::input_port<0>(middle).predecessor_count() == 1 && mp0_list.size() == 1 && *(mp0_list.begin()) == ins[1], "expected 1 predecessor" ); |
199 | ASSERT( tbb::flow::input_port<1>(middle).predecessor_count() == 1 && mp1_list.size() == 1 && *(mp1_list.begin()) == ins[2], "expected 1 predecessor" ); |
200 | ASSERT( middle.successor_count() == 1 && ms_list.size() == 1 && *(ms_list.begin()) == outs[1], "expected 1 successor" ); |
201 | ASSERT( out0.predecessor_count() == 0 && out0_p_list.size() == 0, "expected 0 predecessors" ); |
202 | ASSERT( out0.successor_count() == 0 && out0_s_list.size() == 0, "expected 0 successors" ); |
203 | ASSERT( out1.predecessor_count() == 1 && out1_p_list.size() == 1 && *(out1_p_list.begin()) == mp_ptr, "expected 1 predecessor" ); |
204 | ASSERT( out1.successor_count() == 0 && out1_s_list.size() == 0, "expected 0 successors" ); |
205 | |
206 | in0.try_put(1); |
207 | in1.try_put(2); |
208 | in2.try_put(8); |
209 | in2.try_put(4); |
210 | g.wait_for_all(); |
211 | |
212 | T v_in; |
213 | typename my_node_t::output_type v; |
214 | |
215 | ASSERT( in0.try_get(v_in) == true && v_in == 1, "buffer should have a value of 1" ); |
216 | ASSERT( in1.try_get(v_in) == false, "buffer should not have a value" ); |
217 | ASSERT( out0.try_get(v) == false, "buffer should not have a value" ); |
218 | ASSERT( in0.try_get(v_in) == false, "buffer should not have a value" ); |
219 | |
220 | T r = 0; |
221 | while ( out1.try_get(v) ) { |
222 | check_output(r,v); |
223 | g.wait_for_all(); |
224 | } |
225 | ASSERT( r == 14, "not all values received" ); |
226 | g.wait_for_all(); |
227 | } |
228 | |
229 | void validate_empty_graph() { |
230 | /* in0 */ |
231 | /* */ |
232 | /* port0 out0 */ |
233 | /* | */ |
234 | /* in1 middle */ |
235 | /* | */ |
236 | /* in2 port1 out1 */ |
237 | set_up_lists(); |
238 | |
239 | ASSERT( in0.predecessor_count() == 0 && in0_p_list.size() == 0, "expected 0 predecessors" ); |
240 | ASSERT( in0.successor_count() == 0 && in0_s_list.size() == 0, "expected 0 successors" ); |
241 | ASSERT( in1.predecessor_count() == 0 && in1_p_list.size() == 0, "expected 0 predecessors" ); |
242 | ASSERT( in1.successor_count() == 0 && in1_s_list.size() == 0, "expected 0 successors" ); |
243 | ASSERT( in2.predecessor_count() == 0 && in2_p_list.size() == 0, "expected 0 predecessors" ); |
244 | ASSERT( in2.successor_count() == 0 && in2_s_list.size() == 0, "expected 0 successors" ); |
245 | ASSERT( tbb::flow::input_port<0>(middle).predecessor_count() == 0 && mp0_list.size() == 0, "expected 0 predecessors" ); |
246 | ASSERT( tbb::flow::input_port<1>(middle).predecessor_count() == 0 && mp1_list.size() == 0, "expected 0 predecessors" ); |
247 | ASSERT( middle.successor_count() == 0 && ms_list.size() == 0, "expected 0 successors" ); |
248 | ASSERT( out0.predecessor_count() == 0 && out0_p_list.size() == 0, "expected 0 predecessors" ); |
249 | ASSERT( out0.successor_count() == 0 && out0_s_list.size() == 0, "expected 0 successors" ); |
250 | ASSERT( out1.predecessor_count() == 0 && out1_p_list.size() == 0, "expected 0 predecessors" ); |
251 | ASSERT( out1.successor_count() == 0 && out1_s_list.size() == 0, "expected 0 successors" ); |
252 | |
253 | in0.try_put(1); |
254 | in1.try_put(2); |
255 | in2.try_put(8); |
256 | in2.try_put(4); |
257 | g.wait_for_all(); |
258 | |
259 | T v_in; |
260 | typename my_node_t::output_type v; |
261 | |
262 | ASSERT( in0.try_get(v_in) == true && v_in == 1, "buffer should have a value of 1" ); |
263 | ASSERT( in1.try_get(v_in) == true && v_in == 2, "buffer should have a value of 2" ); |
264 | ASSERT( in2.try_get(v_in) == true && v_in == 8, "buffer should have a value of 8" ); |
265 | ASSERT( in2.try_get(v_in) == true && v_in == 4, "buffer should have a value of 4" ); |
266 | ASSERT( out0.try_get(v) == false, "buffer should not have a value" ); |
267 | ASSERT( out1.try_get(v) == false, "buffer should not have a value" ); |
268 | g.wait_for_all(); |
269 | g.reset(); // NOTE: this should not be necessary!!!!! But it is!!!! |
270 | } |
271 | |
272 | public: |
273 | |
274 | test_indexer_extract() : in0(g), in1(g), in2(g), middle(g), out0(g), out1(g) { |
275 | ins[0] = &in0; |
276 | ins[1] = &in1; |
277 | ins[2] = &in2; |
278 | outs[0] = &out0; |
279 | outs[1] = &out1; |
280 | ms_p0_ptr = static_cast< typename in_node_t::successor_type * >(&tbb::flow::input_port<0>(middle)); |
281 | ms_p1_ptr = static_cast< typename in_node_t::successor_type * >(&tbb::flow::input_port<1>(middle)); |
282 | mp_ptr = static_cast< typename out_node_t::predecessor_type *>(&middle); |
283 | } |
284 | |
285 | virtual ~test_indexer_extract() {} |
286 | |
287 | void run_tests() { |
288 | REMARK("full graph\n" ); |
289 | make_and_validate_full_graph(); |
290 | |
291 | in0.extract(); |
292 | out0.extract(); |
293 | REMARK("partial graph\n" ); |
294 | validate_partial_graph(); |
295 | |
296 | in1.extract(); |
297 | in2.extract(); |
298 | out1.extract(); |
299 | REMARK("empty graph\n" ); |
300 | validate_empty_graph(); |
301 | |
302 | REMARK("full graph\n" ); |
303 | make_and_validate_full_graph(); |
304 | |
305 | middle.extract(); |
306 | REMARK("empty graph\n" ); |
307 | validate_empty_graph(); |
308 | |
309 | REMARK("full graph\n" ); |
310 | make_and_validate_full_graph(); |
311 | |
312 | in0.extract(); |
313 | in1.extract(); |
314 | in2.extract(); |
315 | middle.extract(); |
316 | REMARK("empty graph\n" ); |
317 | validate_empty_graph(); |
318 | |
319 | REMARK("full graph\n" ); |
320 | make_and_validate_full_graph(); |
321 | |
322 | out0.extract(); |
323 | out1.extract(); |
324 | middle.extract(); |
325 | REMARK("empty graph\n" ); |
326 | validate_empty_graph(); |
327 | |
328 | REMARK("full graph\n" ); |
329 | make_and_validate_full_graph(); |
330 | } |
331 | }; |
332 | #endif |
333 | |
334 | const int Count = 150; |
335 | const int MaxPorts = 10; |
336 | const int MaxNSources = 5; // max # of source_nodes to register for each indexer_node input in parallel test |
337 | bool outputCheck[MaxPorts][Count]; // for checking output |
338 | |
339 | void |
340 | check_outputCheck( int nUsed, int maxCnt) { |
341 | for(int i=0; i < nUsed; ++i) { |
342 | for( int j = 0; j < maxCnt; ++j) { |
343 | ASSERT(outputCheck[i][j], NULL); |
344 | } |
345 | } |
346 | } |
347 | |
348 | void |
349 | reset_outputCheck( int nUsed, int maxCnt) { |
350 | for(int i=0; i < nUsed; ++i) { |
351 | for( int j = 0; j < maxCnt; ++j) { |
352 | outputCheck[i][j] = false; |
353 | } |
354 | } |
355 | } |
356 | |
357 | class test_class { |
358 | public: |
359 | test_class() { my_val = 0; } |
360 | test_class(int i) { my_val = i; } |
361 | operator int() { return my_val; } |
362 | private: |
363 | int my_val; |
364 | }; |
365 | |
366 | template<typename T> |
367 | class name_of { |
368 | public: |
369 | static const char* name() { return "Unknown" ; } |
370 | }; |
371 | template<> |
372 | class name_of<int> { |
373 | public: |
374 | static const char* name() { return "int" ; } |
375 | }; |
376 | template<> |
377 | class name_of<float> { |
378 | public: |
379 | static const char* name() { return "float" ; } |
380 | }; |
381 | template<> |
382 | class name_of<double> { |
383 | public: |
384 | static const char* name() { return "double" ; } |
385 | }; |
386 | template<> |
387 | class name_of<long> { |
388 | public: |
389 | static const char* name() { return "long" ; } |
390 | }; |
391 | template<> |
392 | class name_of<short> { |
393 | public: |
394 | static const char* name() { return "short" ; } |
395 | }; |
396 | template<> |
397 | class name_of<test_class> { |
398 | public: |
399 | static const char* name() { return "test_class" ; } |
400 | }; |
401 | |
402 | // TT must be arithmetic, and shouldn't wrap around for reasonable sizes of Count (which is now 150, and maxPorts is 10, |
403 | // so the max number generated right now is 1500 or so.) Source will generate a series of TT with value |
404 | // (init_val + (i-1)*addend) * my_mult, where i is the i-th invocation of the body. We are attaching addend |
405 | // source nodes to a indexer_port, and each will generate part of the numerical series the port is expecting |
406 | // to receive. If there is only one source node, the series order will be maintained; if more than one, |
407 | // this is not guaranteed. |
408 | // The manual specifies bodies can be assigned, so we can't hide the operator=. |
409 | template<typename TT> |
410 | class source_body { |
411 | TT my_mult; |
412 | int my_count; |
413 | int addend; |
414 | public: |
415 | source_body(TT multiplier, int init_val, int addto) : my_mult(multiplier), my_count(init_val), addend(addto) { } |
416 | bool operator()( TT &v) { |
417 | int lc = my_count; |
418 | v = my_mult * (TT)my_count; |
419 | my_count += addend; |
420 | return lc < Count; |
421 | } |
422 | }; |
423 | |
424 | // allocator for indexer_node. |
425 | |
426 | template<typename IType> |
427 | class makeIndexer { |
428 | public: |
429 | static IType *create() { |
430 | IType *temp = new IType(); |
431 | return temp; |
432 | } |
433 | static void destroy(IType *p) { delete p; } |
434 | }; |
435 | |
436 | template<int ELEM, typename INT> |
437 | struct getval_helper { |
438 | |
439 | typedef typename INT::output_type OT; |
440 | typedef typename tbb::flow::tuple_element<ELEM-1, typename INT::tuple_types>::type stored_type; |
441 | |
442 | static int get_integer_val(OT const &o) { |
443 | stored_type res = tbb::flow::cast_to<stored_type>(o); |
444 | return (int)res; |
445 | } |
446 | }; |
447 | |
448 | // holder for source_node pointers for eventual deletion |
449 | |
450 | static void* all_source_nodes[MaxPorts][MaxNSources]; |
451 | |
452 | template<int ELEM, typename INT> |
453 | class source_node_helper { |
454 | public: |
455 | typedef INT indexer_node_type; |
456 | typedef typename indexer_node_type::output_type TT; |
457 | typedef typename tbb::flow::tuple_element<ELEM-1,typename INT::tuple_types>::type IT; |
458 | typedef typename tbb::flow::source_node<IT> my_source_node_type; |
459 | static void () { |
460 | source_node_helper<ELEM-1,INT>::print_remark(); |
461 | REMARK(", %s" , name_of<IT>::name()); |
462 | } |
463 | static void add_source_nodes(indexer_node_type &my_indexer, tbb::flow::graph &g, int nInputs) { |
464 | for(int i=0; i < nInputs; ++i) { |
465 | my_source_node_type *new_node = new my_source_node_type(g, source_body<IT>((IT)(ELEM+1), i, nInputs)); |
466 | tbb::flow::make_edge(*new_node, tbb::flow::input_port<ELEM-1>(my_indexer)); |
467 | #if TBB_DEPRECATED_FLOW_NODE_EXTRACTION |
468 | ASSERT(new_node->successor_count() == 1, NULL); |
469 | #endif |
470 | all_source_nodes[ELEM-1][i] = (void *)new_node; |
471 | } |
472 | #if TBB_DEPRECATED_FLOW_NODE_EXTRACTION |
473 | ASSERT(tbb::flow::input_port<ELEM-1>(my_indexer).predecessor_count() == (size_t)nInputs, NULL); |
474 | #endif |
475 | // add the next source_node |
476 | source_node_helper<ELEM-1, INT>::add_source_nodes(my_indexer, g, nInputs); |
477 | } |
478 | static void check_value(TT &v) { |
479 | if(v.tag() == ELEM-1) { |
480 | int ival = getval_helper<ELEM,INT>::get_integer_val(v); |
481 | ASSERT(!(ival%(ELEM+1)), NULL); |
482 | ival /= (ELEM+1); |
483 | ASSERT(!outputCheck[ELEM-1][ival], NULL); |
484 | outputCheck[ELEM-1][ival] = true; |
485 | } |
486 | else { |
487 | source_node_helper<ELEM-1,INT>::check_value(v); |
488 | } |
489 | } |
490 | |
491 | static void remove_source_nodes(indexer_node_type& my_indexer, int nInputs) { |
492 | for(int i=0; i< nInputs; ++i) { |
493 | my_source_node_type *dp = reinterpret_cast<my_source_node_type *>(all_source_nodes[ELEM-1][i]); |
494 | tbb::flow::remove_edge(*dp, tbb::flow::input_port<ELEM-1>(my_indexer)); |
495 | delete dp; |
496 | } |
497 | source_node_helper<ELEM-1, INT>::remove_source_nodes(my_indexer, nInputs); |
498 | } |
499 | }; |
500 | |
501 | template<typename INT> |
502 | class source_node_helper<1, INT> { |
503 | typedef INT indexer_node_type; |
504 | typedef typename indexer_node_type::output_type TT; |
505 | typedef typename tbb::flow::tuple_element<0, typename INT::tuple_types>::type IT; |
506 | typedef typename tbb::flow::source_node<IT> my_source_node_type; |
507 | public: |
508 | static void () { |
509 | REMARK("Parallel test of indexer_node< %s" , name_of<IT>::name()); |
510 | } |
511 | static void add_source_nodes(indexer_node_type &my_indexer, tbb::flow::graph &g, int nInputs) { |
512 | for(int i=0; i < nInputs; ++i) { |
513 | my_source_node_type *new_node = new my_source_node_type(g, source_body<IT>((IT)2, i, nInputs)); |
514 | tbb::flow::make_edge(*new_node, tbb::flow::input_port<0>(my_indexer)); |
515 | all_source_nodes[0][i] = (void *)new_node; |
516 | } |
517 | } |
518 | static void check_value(TT &v) { |
519 | int ival = getval_helper<1,INT>::get_integer_val(v); |
520 | ASSERT(!(ival%2), NULL); |
521 | ival /= 2; |
522 | ASSERT(!outputCheck[0][ival], NULL); |
523 | outputCheck[0][ival] = true; |
524 | } |
525 | static void remove_source_nodes(indexer_node_type& my_indexer, int nInputs) { |
526 | for(int i=0; i < nInputs; ++i) { |
527 | my_source_node_type *dp = reinterpret_cast<my_source_node_type *>(all_source_nodes[0][i]); |
528 | tbb::flow::remove_edge(*dp, tbb::flow::input_port<0>(my_indexer)); |
529 | delete dp; |
530 | } |
531 | } |
532 | }; |
533 | |
534 | template<typename IType> |
535 | class parallel_test { |
536 | public: |
537 | typedef typename IType::output_type TType; |
538 | typedef typename IType::tuple_types union_types; |
539 | static const int SIZE = tbb::flow::tuple_size<union_types>::value; |
540 | static void test() { |
541 | TType v; |
542 | source_node_helper<SIZE,IType>::print_remark(); |
543 | REMARK(" >\n" ); |
544 | for(int i=0; i < MaxPorts; ++i) { |
545 | for(int j=0; j < MaxNSources; ++j) { |
546 | all_source_nodes[i][j] = NULL; |
547 | } |
548 | } |
549 | for(int nInputs = 1; nInputs <= MaxNSources; ++nInputs) { |
550 | tbb::flow::graph g; |
551 | IType* my_indexer = new IType(g); //makeIndexer<IType>::create(); |
552 | tbb::flow::queue_node<TType> outq1(g); |
553 | tbb::flow::queue_node<TType> outq2(g); |
554 | |
555 | tbb::flow::make_edge(*my_indexer, outq1); |
556 | tbb::flow::make_edge(*my_indexer, outq2); |
557 | |
558 | source_node_helper<SIZE, IType>::add_source_nodes((*my_indexer), g, nInputs); |
559 | |
560 | g.wait_for_all(); |
561 | |
562 | reset_outputCheck(SIZE, Count); |
563 | for(int i=0; i < Count*SIZE; ++i) { |
564 | ASSERT(outq1.try_get(v), NULL); |
565 | source_node_helper<SIZE, IType>::check_value(v); |
566 | } |
567 | |
568 | check_outputCheck(SIZE, Count); |
569 | reset_outputCheck(SIZE, Count); |
570 | |
571 | for(int i=0; i < Count*SIZE; i++) { |
572 | ASSERT(outq2.try_get(v), NULL);; |
573 | source_node_helper<SIZE, IType>::check_value(v); |
574 | } |
575 | check_outputCheck(SIZE, Count); |
576 | |
577 | ASSERT(!outq1.try_get(v), NULL); |
578 | ASSERT(!outq2.try_get(v), NULL); |
579 | |
580 | source_node_helper<SIZE, IType>::remove_source_nodes((*my_indexer), nInputs); |
581 | tbb::flow::remove_edge(*my_indexer, outq1); |
582 | tbb::flow::remove_edge(*my_indexer, outq2); |
583 | makeIndexer<IType>::destroy(my_indexer); |
584 | } |
585 | } |
586 | }; |
587 | |
588 | std::vector<int> last_index_seen; |
589 | |
590 | template<int ELEM, typename IType> |
591 | class serial_queue_helper { |
592 | public: |
593 | typedef typename IType::output_type OT; |
594 | typedef typename IType::tuple_types TT; |
595 | typedef typename tbb::flow::tuple_element<ELEM-1,TT>::type IT; |
596 | static void () { |
597 | serial_queue_helper<ELEM-1,IType>::print_remark(); |
598 | REMARK(", %s" , name_of<IT>::name()); |
599 | } |
600 | static void fill_one_queue(int maxVal, IType &my_indexer) { |
601 | // fill queue to "left" of me |
602 | serial_queue_helper<ELEM-1,IType>::fill_one_queue(maxVal,my_indexer); |
603 | for(int i = 0; i < maxVal; ++i) { |
604 | ASSERT(tbb::flow::input_port<ELEM-1>(my_indexer).try_put((IT)(i*(ELEM+1))), NULL); |
605 | } |
606 | } |
607 | static void put_one_queue_val(int myVal, IType &my_indexer) { |
608 | // put this val to my "left". |
609 | serial_queue_helper<ELEM-1,IType>::put_one_queue_val(myVal, my_indexer); |
610 | ASSERT(tbb::flow::input_port<ELEM-1>(my_indexer).try_put((IT)(myVal*(ELEM+1))), NULL); |
611 | } |
612 | static void check_queue_value(OT &v) { |
613 | if(ELEM - 1 == v.tag()) { |
614 | // this assumes each or node input is queueing. |
615 | int rval = getval_helper<ELEM,IType>::get_integer_val(v); |
616 | ASSERT( rval == (last_index_seen[ELEM-1]+1)*(ELEM+1), NULL); |
617 | last_index_seen[ELEM-1] = rval / (ELEM+1); |
618 | } |
619 | else { |
620 | serial_queue_helper<ELEM-1,IType>::check_queue_value(v); |
621 | } |
622 | } |
623 | }; |
624 | |
625 | template<typename IType> |
626 | class serial_queue_helper<1, IType> { |
627 | public: |
628 | typedef typename IType::output_type OT; |
629 | typedef typename IType::tuple_types TT; |
630 | typedef typename tbb::flow::tuple_element<0,TT>::type IT; |
631 | static void () { |
632 | REMARK("Serial test of indexer_node< %s" , name_of<IT>::name()); |
633 | } |
634 | static void fill_one_queue(int maxVal, IType &my_indexer) { |
635 | for(int i = 0; i < maxVal; ++i) { |
636 | ASSERT(tbb::flow::input_port<0>(my_indexer).try_put((IT)(i*2)), NULL); |
637 | } |
638 | } |
639 | static void put_one_queue_val(int myVal, IType &my_indexer) { |
640 | ASSERT(tbb::flow::input_port<0>(my_indexer).try_put((IT)(myVal*2)), NULL); |
641 | } |
642 | static void check_queue_value(OT &v) { |
643 | ASSERT(v.tag() == 0, NULL); // won't get here unless true |
644 | int rval = getval_helper<1,IType>::get_integer_val(v); |
645 | ASSERT( rval == (last_index_seen[0]+1)*2, NULL); |
646 | last_index_seen[0] = rval / 2; |
647 | } |
648 | }; |
649 | |
650 | template<typename IType, typename TType, int SIZE> |
651 | void test_one_serial( IType &my_indexer, tbb::flow::graph &g) { |
652 | last_index_seen.clear(); |
653 | for(int ii=0; ii < SIZE; ++ii) last_index_seen.push_back(-1); |
654 | |
655 | typedef TType q3_input_type; |
656 | tbb::flow::queue_node< q3_input_type > q3(g); |
657 | q3_input_type v; |
658 | |
659 | tbb::flow::make_edge(my_indexer, q3); |
660 | #if TBB_DEPRECATED_FLOW_NODE_EXTRACTION |
661 | ASSERT(my_indexer.successor_count() == 1, NULL); |
662 | ASSERT(tbb::flow::input_port<0>(my_indexer).predecessor_count() == 0, NULL); |
663 | #endif |
664 | |
665 | // fill each queue with its value one-at-a-time |
666 | for (int i = 0; i < Count; ++i ) { |
667 | serial_queue_helper<SIZE,IType>::put_one_queue_val(i,my_indexer); |
668 | } |
669 | |
670 | g.wait_for_all(); |
671 | for (int i = 0; i < Count * SIZE; ++i ) { |
672 | g.wait_for_all(); |
673 | ASSERT(q3.try_get( v ), "Error in try_get()" ); |
674 | { |
675 | serial_queue_helper<SIZE,IType>::check_queue_value(v); |
676 | } |
677 | } |
678 | ASSERT(!q3.try_get( v ), "extra values in output queue" ); |
679 | for(int ii=0; ii < SIZE; ++ii) last_index_seen[ii] = -1; |
680 | |
681 | // fill each queue completely before filling the next. |
682 | serial_queue_helper<SIZE, IType>::fill_one_queue(Count,my_indexer); |
683 | |
684 | g.wait_for_all(); |
685 | for (int i = 0; i < Count*SIZE; ++i ) { |
686 | g.wait_for_all(); |
687 | ASSERT(q3.try_get( v ), "Error in try_get()" ); |
688 | { |
689 | serial_queue_helper<SIZE,IType>::check_queue_value(v); |
690 | } |
691 | } |
692 | ASSERT(!q3.try_get( v ), "extra values in output queue" ); |
693 | } |
694 | |
695 | // |
696 | // Single predecessor at each port, single accepting successor |
697 | // * put to buffer before port0, then put to buffer before port1, ... |
698 | // * fill buffer before port0 then fill buffer before port1, ... |
699 | |
700 | template<typename IType> |
701 | class serial_test { |
702 | typedef typename IType::output_type TType; // this is the union |
703 | typedef typename IType::tuple_types union_types; |
704 | static const int SIZE = tbb::flow::tuple_size<union_types>::value; |
705 | public: |
706 | static void test() { |
707 | tbb::flow::graph g; |
708 | static const int ELEMS = 3; |
709 | IType* my_indexer = new IType(g); //makeIndexer<IType>::create(g); |
710 | |
711 | test_input_ports_return_ref(*my_indexer); |
712 | |
713 | serial_queue_helper<SIZE, IType>::print_remark(); REMARK(" >\n" ); |
714 | |
715 | test_one_serial<IType,TType,SIZE>(*my_indexer, g); |
716 | |
717 | std::vector<IType> indexer_vector(ELEMS,*my_indexer); |
718 | |
719 | makeIndexer<IType>::destroy(my_indexer); |
720 | |
721 | for(int e = 0; e < ELEMS; ++e) { |
722 | test_one_serial<IType,TType,SIZE>(indexer_vector[e], g); |
723 | } |
724 | } |
725 | |
726 | }; // serial_test |
727 | |
728 | template< |
729 | template<typename> class TestType, // serial_test or parallel_test |
730 | typename T0, typename T1=void, typename T2=void, typename T3=void, typename T4=void, |
731 | typename T5=void, typename T6=void, typename T7=void, typename T8=void, typename T9=void> // type of the inputs to the indexer_node |
732 | class generate_test { |
733 | public: |
734 | typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> indexer_node_type; |
735 | static void do_test() { |
736 | TestType<indexer_node_type>::test(); |
737 | } |
738 | }; |
739 | |
740 | //specializations for indexer node inputs |
741 | template< |
742 | template<typename> class TestType, |
743 | typename T0, typename T1, typename T2, typename T3, typename T4, |
744 | typename T5, typename T6, typename T7, typename T8> |
745 | class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6, T7, T8> { |
746 | public: |
747 | typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6, T7, T8> indexer_node_type; |
748 | static void do_test() { |
749 | TestType<indexer_node_type>::test(); |
750 | } |
751 | }; |
752 | |
753 | template< |
754 | template<typename> class TestType, |
755 | typename T0, typename T1, typename T2, typename T3, typename T4, |
756 | typename T5, typename T6, typename T7> |
757 | class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6, T7> { |
758 | public: |
759 | typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6, T7> indexer_node_type; |
760 | static void do_test() { |
761 | TestType<indexer_node_type>::test(); |
762 | } |
763 | }; |
764 | |
765 | template< |
766 | template<typename> class TestType, |
767 | typename T0, typename T1, typename T2, typename T3, typename T4, |
768 | typename T5, typename T6> |
769 | class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6> { |
770 | public: |
771 | typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6> indexer_node_type; |
772 | static void do_test() { |
773 | TestType<indexer_node_type>::test(); |
774 | } |
775 | }; |
776 | |
777 | template< |
778 | template<typename> class TestType, |
779 | typename T0, typename T1, typename T2, typename T3, typename T4, |
780 | typename T5> |
781 | class generate_test<TestType, T0, T1, T2, T3, T4, T5> { |
782 | public: |
783 | typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5> indexer_node_type; |
784 | static void do_test() { |
785 | TestType<indexer_node_type>::test(); |
786 | } |
787 | }; |
788 | |
789 | template< |
790 | template<typename> class TestType, |
791 | typename T0, typename T1, typename T2, typename T3, typename T4> |
792 | class generate_test<TestType, T0, T1, T2, T3, T4> { |
793 | public: |
794 | typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4> indexer_node_type; |
795 | static void do_test() { |
796 | TestType<indexer_node_type>::test(); |
797 | } |
798 | }; |
799 | |
800 | template< |
801 | template<typename> class TestType, |
802 | typename T0, typename T1, typename T2, typename T3> |
803 | class generate_test<TestType, T0, T1, T2, T3> { |
804 | public: |
805 | typedef tbb::flow::indexer_node<T0, T1, T2, T3> indexer_node_type; |
806 | static void do_test() { |
807 | TestType<indexer_node_type>::test(); |
808 | } |
809 | }; |
810 | |
811 | template< |
812 | template<typename> class TestType, |
813 | typename T0, typename T1, typename T2> |
814 | class generate_test<TestType, T0, T1, T2> { |
815 | public: |
816 | typedef tbb::flow::indexer_node<T0, T1, T2> indexer_node_type; |
817 | static void do_test() { |
818 | TestType<indexer_node_type>::test(); |
819 | } |
820 | }; |
821 | |
822 | template< |
823 | template<typename> class TestType, |
824 | typename T0, typename T1> |
825 | class generate_test<TestType, T0, T1> { |
826 | public: |
827 | typedef tbb::flow::indexer_node<T0, T1> indexer_node_type; |
828 | static void do_test() { |
829 | TestType<indexer_node_type>::test(); |
830 | } |
831 | }; |
832 | |
833 | template< |
834 | template<typename> class TestType, |
835 | typename T0> |
836 | class generate_test<TestType, T0> { |
837 | public: |
838 | typedef tbb::flow::indexer_node<T0> indexer_node_type; |
839 | static void do_test() { |
840 | TestType<indexer_node_type>::test(); |
841 | } |
842 | }; |
843 | |
844 | int TestMain() { |
845 | REMARK("Testing indexer_node, " ); |
846 | #if __TBB_USE_TBB_TUPLE |
847 | REMARK("using TBB tuple\n" ); |
848 | #else |
849 | REMARK("using platform tuple\n" ); |
850 | #endif |
851 | |
852 | for (int p = 0; p < 2; ++p) { |
853 | generate_test<serial_test, float>::do_test(); |
854 | #if MAX_TUPLE_TEST_SIZE >= 4 |
855 | generate_test<serial_test, float, double, int>::do_test(); |
856 | #endif |
857 | #if MAX_TUPLE_TEST_SIZE >= 6 |
858 | generate_test<serial_test, double, double, int, long, int, short>::do_test(); |
859 | #endif |
860 | #if MAX_TUPLE_TEST_SIZE >= 8 |
861 | generate_test<serial_test, float, double, double, double, float, int, float, long>::do_test(); |
862 | #endif |
863 | #if MAX_TUPLE_TEST_SIZE >= 10 |
864 | generate_test<serial_test, float, double, int, double, double, float, long, int, float, long>::do_test(); |
865 | #endif |
866 | generate_test<parallel_test, float, double>::do_test(); |
867 | #if MAX_TUPLE_TEST_SIZE >= 3 |
868 | generate_test<parallel_test, float, int, long>::do_test(); |
869 | #endif |
870 | #if MAX_TUPLE_TEST_SIZE >= 5 |
871 | generate_test<parallel_test, double, double, int, int, short>::do_test(); |
872 | #endif |
873 | #if MAX_TUPLE_TEST_SIZE >= 7 |
874 | generate_test<parallel_test, float, int, double, float, long, float, long>::do_test(); |
875 | #endif |
876 | #if MAX_TUPLE_TEST_SIZE >= 9 |
877 | generate_test<parallel_test, float, double, int, double, double, long, int, float, long>::do_test(); |
878 | #endif |
879 | } |
880 | #if TBB_DEPRECATED_FLOW_NODE_EXTRACTION |
881 | test_indexer_extract<int>().run_tests(); |
882 | #endif |
883 | return Harness::Done; |
884 | } |
885 | |