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
33template< typename T >
34class test_indexer_extract {
35protected:
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
272public:
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
334const int Count = 150;
335const int MaxPorts = 10;
336const int MaxNSources = 5; // max # of source_nodes to register for each indexer_node input in parallel test
337bool outputCheck[MaxPorts][Count]; // for checking output
338
339void
340check_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
348void
349reset_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
357class 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
366template<typename T>
367class name_of {
368public:
369 static const char* name() { return "Unknown"; }
370};
371template<>
372class name_of<int> {
373public:
374 static const char* name() { return "int"; }
375};
376template<>
377class name_of<float> {
378public:
379 static const char* name() { return "float"; }
380};
381template<>
382class name_of<double> {
383public:
384 static const char* name() { return "double"; }
385};
386template<>
387class name_of<long> {
388public:
389 static const char* name() { return "long"; }
390};
391template<>
392class name_of<short> {
393public:
394 static const char* name() { return "short"; }
395};
396template<>
397class name_of<test_class> {
398public:
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=.
409template<typename TT>
410class source_body {
411 TT my_mult;
412 int my_count;
413 int addend;
414public:
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
426template<typename IType>
427class makeIndexer {
428public:
429 static IType *create() {
430 IType *temp = new IType();
431 return temp;
432 }
433 static void destroy(IType *p) { delete p; }
434};
435
436template<int ELEM, typename INT>
437struct 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
450static void* all_source_nodes[MaxPorts][MaxNSources];
451
452template<int ELEM, typename INT>
453class source_node_helper {
454public:
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 print_remark() {
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
501template<typename INT>
502class 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;
507public:
508 static void print_remark() {
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
534template<typename IType>
535class parallel_test {
536public:
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
588std::vector<int> last_index_seen;
589
590template<int ELEM, typename IType>
591class serial_queue_helper {
592public:
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 print_remark() {
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
625template<typename IType>
626class serial_queue_helper<1, IType> {
627public:
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 print_remark() {
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
650template<typename IType, typename TType, int SIZE>
651void 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
700template<typename IType>
701class 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;
705public:
706static 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
728template<
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
732class generate_test {
733public:
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
741template<
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>
745class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6, T7, T8> {
746public:
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
753template<
754 template<typename> class TestType,
755 typename T0, typename T1, typename T2, typename T3, typename T4,
756 typename T5, typename T6, typename T7>
757class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6, T7> {
758public:
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
765template<
766 template<typename> class TestType,
767 typename T0, typename T1, typename T2, typename T3, typename T4,
768 typename T5, typename T6>
769class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6> {
770public:
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
777template<
778 template<typename> class TestType,
779 typename T0, typename T1, typename T2, typename T3, typename T4,
780 typename T5>
781class generate_test<TestType, T0, T1, T2, T3, T4, T5> {
782public:
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
789template<
790 template<typename> class TestType,
791 typename T0, typename T1, typename T2, typename T3, typename T4>
792class generate_test<TestType, T0, T1, T2, T3, T4> {
793public:
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
800template<
801 template<typename> class TestType,
802 typename T0, typename T1, typename T2, typename T3>
803class generate_test<TestType, T0, T1, T2, T3> {
804public:
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
811template<
812 template<typename> class TestType,
813 typename T0, typename T1, typename T2>
814class generate_test<TestType, T0, T1, T2> {
815public:
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
822template<
823 template<typename> class TestType,
824 typename T0, typename T1>
825class generate_test<TestType, T0, T1> {
826public:
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
833template<
834 template<typename> class TestType,
835 typename T0>
836class generate_test<TestType, T0> {
837public:
838 typedef tbb::flow::indexer_node<T0> indexer_node_type;
839 static void do_test() {
840 TestType<indexer_node_type>::test();
841 }
842};
843
844int 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