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#include "harness.h"
18#if __TBB_FLOW_GRAPH_CPP11_FEATURES
19
20#include "tbb/flow_graph.h"
21#include "harness_graph.h"
22#include <tuple>
23#include <cmath>
24#include <vector>
25
26struct passthru_body {
27 int operator()( int i ) {
28 return i;
29 }
30};
31
32class src_body{
33 int start;
34 int finish;
35 int step;
36public:
37 src_body(int f, int s) : start(1), finish(f), step(s) {}
38 bool operator()(int &a) {
39 a = start;
40 if (start <= finish) {
41 a = start;
42 start+=step;
43 return true;
44 }
45 else {
46 return false;
47 };
48 }
49};
50
51struct m_fxn_body{
52 void operator()(int, tbb::flow::multifunction_node<int, tbb::flow::tuple<int,int> >::output_ports_type ) {}
53};
54
55struct ct_body {
56ct_body(){}
57 void operator()(tbb::flow::continue_msg){}
58};
59
60struct seq_body {
61int operator()(int i){return i;}
62};
63
64template<int N, typename T1, typename T2>
65struct compare {
66 static void compare_refs(T1 tuple1, T2 tuple2) {
67 ASSERT( &tbb::flow::get<N>(tuple1) == &tbb::flow::get<N>(tuple2), "ports not set correctly");
68 compare<N-1, T1, T2>::compare_refs(tuple1, tuple2);
69 }
70};
71
72template<typename T1, typename T2>
73struct compare<1, T1, T2> {
74 static void compare_refs(T1 tuple1, T2 tuple2) {
75 ASSERT(&tbb::flow::get<0>(tuple1) == &tbb::flow::get<0>(tuple2), "port 0 not correctly set");
76 }
77};
78
79void add_all_nodes (){
80 tbb::flow::graph g;
81
82 typedef tbb::flow::tuple<tbb::flow::continue_msg, tbb::flow::tuple<int, int>, int, int, int, int,
83 int, int, int, int, int, int, int, int > InputTupleType;
84
85 typedef tbb::flow::tuple<tbb::flow::continue_msg, tbb::flow::tuple<int, int>, tbb::flow::tagged_msg<size_t, int, float>,
86 int, int, int, int, int, int, int, int, int, int, int, int > OutputTupleType;
87
88 typedef tbb::flow::tuple< > EmptyTupleType;
89
90 typedef tbb::flow::composite_node<InputTupleType, OutputTupleType > input_output_type;
91 typedef tbb::flow::composite_node<InputTupleType, EmptyTupleType > input_only_type;
92 typedef tbb::flow::composite_node<EmptyTupleType, OutputTupleType > output_only_type;
93
94 const size_t NUM_INPUTS = tbb::flow::tuple_size<InputTupleType>::value;
95 const size_t NUM_OUTPUTS = tbb::flow::tuple_size<OutputTupleType>::value;
96
97 //node types
98 tbb::flow::continue_node<tbb::flow::continue_msg> ct(g, ct_body());
99 tbb::flow::split_node< tbb::flow::tuple<int, int> > s(g);
100 tbb::flow::source_node<int> src(g, src_body(20,5), false);
101 tbb::flow::function_node<int, int> fxn(g, tbb::flow::unlimited, passthru_body());
102 tbb::flow::multifunction_node<int, tbb::flow::tuple<int, int> > m_fxn(g, tbb::flow::unlimited, m_fxn_body());
103 tbb::flow::broadcast_node<int> bc(g);
104 tbb::flow::limiter_node<int> lim(g, 2);
105 tbb::flow::indexer_node<int, float> ind(g);
106 tbb::flow::join_node< tbb::flow::tuple< int, int >, tbb::flow::queueing > j(g);
107 tbb::flow::queue_node<int> q(g);
108 tbb::flow::buffer_node<int> bf(g);
109 tbb::flow::priority_queue_node<int> pq(g);
110 tbb::flow::write_once_node<int> wo(g);
111 tbb::flow::overwrite_node<int> ovw(g);
112 tbb::flow::sequencer_node<int> seq(g, seq_body());
113
114#if !__TBB_UPCAST_OF_TUPLE_OF_REF_BROKEN
115 auto input_tuple = std::tie(ct, s, m_fxn, fxn, bc, tbb::flow::input_port<0>(j), lim, q, tbb::flow::input_port<0>(ind),
116 pq, ovw, wo, bf, seq);
117 auto output_tuple = std::tie(ct,j, ind, fxn, src, bc, tbb::flow::output_port<0>(s), lim, tbb::flow::output_port<0>(m_fxn),
118 q, pq, ovw, wo, bf, seq );
119#else
120 // upcasting from derived to base for a tuple of references created by std::tie
121 // fails on gcc 4.4 (and all icc in that environment)
122 input_output_type::input_ports_type input_tuple(ct, s, m_fxn, fxn, bc, tbb::flow::input_port<0>(j), lim, q,
123 tbb::flow::input_port<0>(ind), pq, ovw, wo, bf, seq);
124
125 input_output_type::output_ports_type output_tuple(ct,j, ind, fxn, src, bc, tbb::flow::output_port<0>(s),
126 lim, tbb::flow::output_port<0>(m_fxn), q, pq, ovw, wo, bf, seq);
127#endif
128
129 //composite_node with both input_ports and output_ports
130 input_output_type a_node(g);
131 a_node.set_external_ports(input_tuple, output_tuple);
132
133 a_node.add_visible_nodes(src, fxn, m_fxn, bc, lim, ind, s, ct, j, q, bf, pq, wo, ovw, seq);
134 a_node.add_nodes(src, fxn, m_fxn, bc, lim, ind, s, ct, j, q, bf, pq, wo, ovw, seq);
135
136 auto a_node_input_ports_ptr = a_node.input_ports();
137 compare<NUM_INPUTS-1, decltype(a_node_input_ports_ptr), decltype(input_tuple)>::compare_refs(a_node_input_ports_ptr, input_tuple);
138 ASSERT (NUM_INPUTS == tbb::flow::tuple_size<decltype(a_node_input_ports_ptr)>::value, "not all declared input ports were bound to nodes");
139
140 auto a_node_output_ports_ptr = a_node.output_ports();
141 compare<NUM_OUTPUTS-1, decltype(a_node_output_ports_ptr), decltype(output_tuple)>::compare_refs(a_node_output_ports_ptr, output_tuple);
142 ASSERT(NUM_OUTPUTS == tbb::flow::tuple_size<decltype(a_node_output_ports_ptr)>::value, "not all declared output ports were bound to nodes");
143
144 //composite_node with only input_ports
145 input_only_type b_node(g);
146 b_node.set_external_ports(input_tuple);
147
148 b_node.add_visible_nodes(src, fxn, m_fxn, bc, lim, ind, s, ct, j, q, bf, pq, wo, ovw, seq);
149 b_node.add_nodes(src, fxn, m_fxn, bc, lim, ind, s, ct, j, q, bf, pq, wo, ovw, seq);
150
151 auto b_node_input_ports_ptr = b_node.input_ports();
152 compare<NUM_INPUTS-1, decltype(b_node_input_ports_ptr), decltype(input_tuple)>::compare_refs(b_node_input_ports_ptr, input_tuple);
153 ASSERT (NUM_INPUTS == tbb::flow::tuple_size<decltype(b_node_input_ports_ptr)>::value, "not all declared input ports were bound to nodes");
154
155 //composite_node with only output_ports
156 output_only_type c_node(g);
157 c_node.set_external_ports(output_tuple);
158
159 c_node.add_visible_nodes(src, fxn, m_fxn, bc, lim, ind, s, ct, j, q, bf, pq, wo, ovw, seq);
160
161 c_node.add_nodes(src, fxn, m_fxn, bc, lim, ind, s, ct, j, q, bf, pq, wo, ovw, seq);
162
163 auto c_node_output_ports_ptr = c_node.output_ports();
164 compare<NUM_OUTPUTS-1, decltype(c_node_output_ports_ptr), decltype(output_tuple)>::compare_refs(c_node_output_ports_ptr, output_tuple);
165 ASSERT (NUM_OUTPUTS == tbb::flow::tuple_size<decltype(c_node_output_ports_ptr)>::value, "not all declared input ports were bound to nodes");
166}
167
168struct tiny_node : public tbb::flow::composite_node< tbb::flow::tuple< int >, tbb::flow::tuple< int > > {
169 tbb::flow::function_node< int, int > f1;
170 tbb::flow::function_node< int, int > f2;
171 typedef tbb::flow::composite_node< tbb::flow::tuple< int >, tbb::flow::tuple< int > > base_type;
172
173public:
174 tiny_node(tbb::flow::graph &g, bool hidden = false) : base_type(g), f1(g, tbb::flow::unlimited, passthru_body() ), f2(g, tbb::flow::unlimited, passthru_body() ) {
175 tbb::flow::make_edge( f1, f2 );
176
177 tbb::flow::tuple<tbb::flow::function_node< int, int >& > input_tuple(f1);
178 tbb::flow::tuple<tbb::flow::function_node< int, int >& > output_tuple(f2);
179 base_type::set_external_ports( input_tuple, output_tuple );
180
181 if(hidden)
182 base_type::add_nodes(f1, f2);
183 else
184 base_type::add_visible_nodes(f1, f2);
185
186 }
187};
188
189int test_tiny(bool hidden = false) {
190 tbb::flow::graph g;
191 tbb::flow::function_node< int, int > f0( g, tbb::flow::unlimited, passthru_body() );
192 tiny_node t(g, hidden);
193 ASSERT(&tbb::flow::input_port<0>(t) == &t.f1, "f1 not bound to input port 0 in composite_node t");
194 ASSERT(&tbb::flow::output_port<0>(t) == &t.f2, "f2 not bound to output port 0 in composite_node t");
195
196 tiny_node t1(g, hidden);
197 ASSERT(&tbb::flow::get<0>(t1.input_ports()) == &t1.f1, "f1 not bound to input port 0 in composite_node t1");
198 ASSERT(&tbb::flow::get<0>(t1.output_ports()) == &t1.f2, "f2 not bound to output port 0 in composite_node t1");
199
200 test_input_ports_return_ref(t1);
201 test_output_ports_return_ref(t1);
202
203 tiny_node t2(g, hidden);
204 ASSERT(&tbb::flow::input_port<0>(t2) == &t2.f1, "f1 not bound to input port 0 in composite_node t2");
205 ASSERT(&tbb::flow::output_port<0>(t2) == &t2.f2, "f2 not bound to output port 0 in composite_node t2");
206
207 tbb::flow::function_node< int, int > f3( g, tbb::flow::unlimited, passthru_body() );
208 tbb::flow::make_edge( f0, t );
209 tbb::flow::make_edge( t, t1 );
210 tbb::flow::make_edge( t1, t2 );
211 tbb::flow::make_edge( t2 , f3 );
212 tbb::flow::queue_node<int> q(g);
213 tbb::flow::make_edge(f3, q);
214 f0.try_put(1);
215 g.wait_for_all();
216
217 int i, j =0;
218 q.try_get(i);
219 ASSERT( i == 1, "item did not go through graph");
220 q.try_get(j);
221 ASSERT( !j, "unexpected item in graph");
222 g.wait_for_all();
223
224 tbb::flow::remove_edge(f3, q);
225 tbb::flow::remove_edge(t2, f3);
226 tbb::flow::remove_edge(t1, t2);
227
228 tbb::flow::make_edge( t1 , f3 );
229 tbb::flow::make_edge(f3, q);
230
231 f0.try_put(2);
232 g.wait_for_all();
233
234 q.try_get(i);
235 ASSERT( i == 2, "item did not go through graph after removal of edge");
236 q.try_get(j);
237 ASSERT( !j, "unexpected item in graph after removal of edge");
238
239 return 0;
240}
241
242class adder_node : public tbb::flow::composite_node< tbb::flow::tuple< int, int >, tbb::flow::tuple< int > > {
243public:
244 tbb::flow::join_node< tbb::flow::tuple< int, int >, tbb::flow::queueing > j;
245 tbb::flow::function_node< tbb::flow::tuple< int, int >, int > f;
246private:
247 typedef tbb::flow::composite_node< tbb::flow::tuple< int, int >, tbb::flow::tuple< int > > base_type;
248
249 struct f_body {
250 int operator()( const tbb::flow::tuple< int, int > &t ) {
251 return tbb::flow::get<0>(t) + tbb::flow::get<1>(t);
252 }
253 };
254
255public:
256 adder_node(tbb::flow::graph &g, bool hidden = false) : base_type(g), j(g), f(g, tbb::flow::unlimited, f_body() ) {
257 tbb::flow::make_edge( j, f );
258
259 base_type::set_external_ports(base_type::input_ports_type(tbb::flow::input_port<0>(j), tbb::flow::input_port<1>(j)), base_type::output_ports_type(f));
260
261 if (hidden)
262 base_type::add_nodes(j, f);
263 else
264 base_type::add_visible_nodes(j, f);
265
266 }
267};
268
269struct square_body { int operator()(int v) { return v*v; } };
270struct cube_body { int operator()(int v) { return v*v*v; } };
271int adder_sum(int i) {
272 return (int)(pow(3*pow(i,3) + pow(i, 2),2));
273}
274int test_adder(bool hidden = false) {
275 tbb::flow::graph g;
276 tbb::flow::function_node<int,int> s(g, tbb::flow::unlimited, square_body());
277 tbb::flow::function_node<int,int> c(g, tbb::flow::unlimited, cube_body());
278 tbb::flow::function_node<int,int> p(g, tbb::flow::unlimited, passthru_body());
279
280 adder_node a0(g, hidden);
281 ASSERT(&tbb::flow::input_port<0>(a0) == &tbb::flow::input_port<0>(a0.j), "input_port 0 of j not bound to input port 0 in composite_node a0");
282 ASSERT(&tbb::flow::input_port<1>(a0) == &tbb::flow::input_port<1>(a0.j), "input_port 1 of j not bound to input port 1 in composite_node a0");
283 ASSERT(&tbb::flow::output_port<0>(a0) == &a0.f, "f not bound to output port 0 in composite_node a0");
284
285 adder_node a1(g, hidden);
286 ASSERT(&tbb::flow::get<0>(a0.input_ports()) == &tbb::flow::input_port<0>(a0.j), "input_port 0 of j not bound to input port 0 in composite_node a1");
287 ASSERT(&tbb::flow::get<1>(a0.input_ports()) == &tbb::flow::input_port<1>(a0.j), "input_port1 of j not bound to input port 1 in composite_node a1");
288 ASSERT(&tbb::flow::get<0>(a0.output_ports()) == &a0.f, "f not bound to output port 0 in composite_node a1");
289
290 adder_node a2(g, hidden);
291 ASSERT(&tbb::flow::input_port<0>(a2) == &tbb::flow::input_port<0>(a2.j), "input_port 0 of j not bound to input port 0 in composite_node a2");
292 ASSERT(&tbb::flow::input_port<1>(a2) == &tbb::flow::input_port<1>(a2.j), "input_port 1 of j not bound to input port 1 in composite_node a2");
293 ASSERT(&tbb::flow::output_port<0>(a2) == &a2.f, "f not bound to output port 0 in composite_node a2");
294
295 adder_node a3(g, hidden);
296 ASSERT(&tbb::flow::get<0>(a3.input_ports()) == &tbb::flow::input_port<0>(a3.j), "input_port 0 of j not bound to input port 0 in composite_node a3");
297 ASSERT(&tbb::flow::get<1>(a3.input_ports()) == &tbb::flow::input_port<1>(a3.j), "input_port1 of j not bound to input port 1 in composite_node a3");
298 ASSERT(&tbb::flow::get<0>(a3.output_ports()) == &a3.f, "f not bound to output port 0 in composite_node a3");
299
300 tbb::flow::function_node<int,int> s2(g, tbb::flow::unlimited, square_body());
301 tbb::flow::queue_node<int> q(g);
302
303 tbb::flow::make_edge( s, tbb::flow::input_port<0>(a0) );
304 tbb::flow::make_edge( c, tbb::flow::input_port<1>(a0) );
305
306 tbb::flow::make_edge( c, tbb::flow::input_port<0>(a1) );
307 tbb::flow::make_edge( c, tbb::flow::input_port<1>(a1) );
308
309 tbb::flow::make_edge( tbb::flow::output_port<0>(a0), tbb::flow::input_port<0>(a2) );
310 tbb::flow::make_edge( tbb::flow::output_port<0>(a1), tbb::flow::input_port<1>(a2) );
311
312 tbb::flow::make_edge( tbb::flow::output_port<0>(a2), s2 );
313 tbb::flow::make_edge( s2, q );
314
315 int sum_total=0;
316 int result=0;
317 for ( int i = 1; i < 4; ++i ) {
318 s.try_put(i);
319 c.try_put(i);
320 sum_total += adder_sum(i);
321 g.wait_for_all();
322 }
323
324 int j;
325 for ( int i = 1; i < 4; ++i ) {
326 q.try_get(j);
327 result += j;
328 }
329 g.wait_for_all();
330 ASSERT(result == sum_total, "the sum from the graph does not match the calculated value");
331
332 tbb::flow::remove_edge(s2, q);
333 tbb::flow::remove_edge( a2, s2 );
334 tbb::flow::make_edge( a0, a3 );
335 tbb::flow::make_edge( a1, tbb::flow::input_port<1>(a3) );
336 tbb::flow::make_edge( a3, s2 );
337 tbb::flow::make_edge( s2, q );
338
339 sum_total=0;
340 result=0;
341 for ( int i = 10; i < 20; ++i ) {
342 s.try_put(i);
343 c.try_put(i);
344 sum_total += adder_sum(i);
345 g.wait_for_all();
346 }
347
348 for ( int i = 10; i < 20; ++i ) {
349 q.try_get(j);
350 result += j;
351 }
352 g.wait_for_all();
353 ASSERT(result == sum_total, "the new sum after the replacement of the nodes does not match the calculated value");
354
355 return 0;
356}
357
358/*
359 outer composite node (outer_node)
360 |-------------------------------------------------------------------|
361 | |
362 | |------------------| |------------------| |------------------| |
363 |---------------------| |--| inner composite | /| inner composite | /| inner composite | | |-------------------|
364 |broadcast node(input)|/| | node |/ | node |/ | node |-+-| queue node(output)|
365 |---------------------|\| |(inner_node1) |\ | (inner_node2) |\ | (inner_node3) | | |-------------------|
366 |--| | \| | \| | |
367 | |------------------| |------------------| |------------------| |
368 | |
369 |-------------------------------------------------------------------|
370
371*/
372int test_nested_adder(bool hidden=false) {
373 tbb::flow::graph g;
374 tbb::flow::composite_node<tbb::flow::tuple<int, int>, tbb::flow::tuple<int> > outer_node(g);
375 typedef tbb::flow::composite_node<tbb::flow::tuple<int, int>, tbb::flow::tuple<int> > base_type;
376 tbb::flow::broadcast_node<int> input(g);
377 tbb::flow::queue_node<int> output(g);
378
379 adder_node inner_node1(g, hidden);
380 adder_node inner_node2(g, hidden);
381 adder_node inner_node3(g, hidden);
382
383 outer_node.set_external_ports(base_type::input_ports_type(tbb::flow::input_port<0>(inner_node1), tbb::flow::input_port<1>(inner_node1)), base_type::output_ports_type(tbb::flow::output_port<0>(inner_node3)));
384
385 ASSERT(&tbb::flow::input_port<0>(outer_node) == &tbb::flow::input_port<0>(inner_node1), "input port 0 of inner_node1 not bound to input port 0 in outer_node");
386 ASSERT(&tbb::flow::input_port<1>(outer_node) == &tbb::flow::input_port<1>(inner_node1), "input port 1 of inner_node1 not bound to input port 1 in outer_node");
387 ASSERT(&tbb::flow::output_port<0>(outer_node) == &tbb::flow::output_port<0>(inner_node3), "output port 0 of inner_node3 not bound to output port 0 in outer_node");
388
389 tbb::flow::make_edge(input, tbb::flow::input_port<0>(outer_node)/*inner_node1*/);
390 tbb::flow::make_edge(input, tbb::flow::input_port<1>(outer_node)/*inner_node1*/);
391
392 tbb::flow::make_edge(inner_node1, tbb::flow::input_port<0>(inner_node2));
393 tbb::flow::make_edge(inner_node1, tbb::flow::input_port<1>(inner_node2));
394
395 tbb::flow::make_edge(inner_node2, tbb::flow::input_port<0>(inner_node3));
396 tbb::flow::make_edge(inner_node2, tbb::flow::input_port<1>(inner_node3));
397
398 tbb::flow::make_edge(outer_node/*inner_node3*/, output);
399
400 if(hidden)
401 outer_node.add_nodes(inner_node1, inner_node2, inner_node3);
402 else
403 outer_node.add_visible_nodes(inner_node1, inner_node2, inner_node3);
404
405 int out;
406 for (int i = 1; i < 200000; ++i) {
407 input.try_put(i);
408 g.wait_for_all();
409 output.try_get(out);
410 ASSERT(tbb::flow::output_port<0>(outer_node).try_get(out) == output.try_get(out), "output from outer_node does not match output from graph");
411 ASSERT(out == 8*i, "output from outer_node not correct");
412 }
413 g.wait_for_all();
414
415 return 0;
416}
417
418template< typename T >
419class prefix_node : public tbb::flow::composite_node< tbb::flow::tuple< T, T, T, T, T >, tbb::flow::tuple< T, T, T, T, T > > {
420 typedef tbb::flow::tuple< T, T, T, T, T > my_tuple_t;
421public:
422 tbb::flow::join_node< my_tuple_t, tbb::flow::queueing > j;
423 tbb::flow::split_node< my_tuple_t > s;
424private:
425 tbb::flow::function_node< my_tuple_t, my_tuple_t > f;
426 typedef tbb::flow::composite_node< my_tuple_t, my_tuple_t > base_type;
427
428 struct f_body {
429 my_tuple_t operator()( const my_tuple_t &t ) {
430 return my_tuple_t( tbb::flow::get<0>(t),
431 tbb::flow::get<0>(t) + tbb::flow::get<1>(t),
432 tbb::flow::get<0>(t) + tbb::flow::get<1>(t) + tbb::flow::get<2>(t),
433 tbb::flow::get<0>(t) + tbb::flow::get<1>(t) + tbb::flow::get<2>(t) + tbb::flow::get<3>(t),
434 tbb::flow::get<0>(t) + tbb::flow::get<1>(t) + tbb::flow::get<2>(t) + tbb::flow::get<3>(t) + tbb::flow::get<4>(t) );
435 }
436 };
437
438public:
439 prefix_node(tbb::flow::graph &g, bool hidden = false ) : base_type(g), j(g), s(g), f(g, tbb::flow::serial, f_body() ) {
440 tbb::flow::make_edge( j, f );
441 tbb::flow::make_edge( f, s );
442
443 typename base_type::input_ports_type input_tuple(tbb::flow::input_port<0>(j), tbb::flow::input_port<1>(j), tbb::flow::input_port<2>(j), tbb::flow::input_port<3>(j), tbb::flow::input_port<4>(j));
444
445 typename base_type::output_ports_type output_tuple(tbb::flow::output_port<0>(s), tbb::flow::output_port<1>(s), tbb::flow::output_port<2>(s), tbb::flow::output_port<3>(s), tbb::flow::output_port<4>(s));
446
447 base_type::set_external_ports(input_tuple, output_tuple);
448
449 if(hidden)
450 base_type::add_nodes(j,s,f);
451 else
452 base_type::add_visible_nodes(j,s,f);
453
454 }
455};
456
457int test_prefix(bool hidden = false) {
458 tbb::flow::graph g;
459 prefix_node<double> p(g, hidden);
460
461 ASSERT(&tbb::flow::get<0>(p.input_ports()) == &tbb::flow::input_port<0>(p.j), "input port 0 of j is not bound to input port 0 of composite node p");
462 ASSERT(&tbb::flow::input_port<1>(p.j) == &tbb::flow::input_port<1>(p.j), "input port 1 of j is not bound to input port 1 of composite node p");
463 ASSERT(&tbb::flow::get<2>(p.input_ports()) == &tbb::flow::input_port<2>(p.j), "input port 2 of j is not bound to input port 2 of composite node p");
464 ASSERT(&tbb::flow::input_port<3>(p.j) == &tbb::flow::input_port<3>(p.j), "input port 3 of j is not bound to input port 3 of composite node p");
465 ASSERT(&tbb::flow::get<4>(p.input_ports()) == &tbb::flow::input_port<4>(p.j), "input port 4 of j is not bound to input port 4 of composite node p");
466
467
468 ASSERT(&tbb::flow::get<0>(p.output_ports()) == &tbb::flow::output_port<0>(p.s), "output port 0 of s is not bound to output port 0 of composite node p");
469 ASSERT(&tbb::flow::output_port<1>(p.s) == &tbb::flow::output_port<1>(p.s), "output port 1 of s is not bound to output port 1 of composite node p");
470 ASSERT(&tbb::flow::get<2>(p.output_ports()) == &tbb::flow::output_port<2>(p.s), "output port 2 of s is not bound to output port 2 of composite node p");
471 ASSERT(&tbb::flow::output_port<3>(p.s) == &tbb::flow::output_port<3>(p.s), "output port 3 of s is not bound to output port 3 of composite node p");
472 ASSERT(&tbb::flow::get<4>(p.output_ports()) == &tbb::flow::output_port<4>(p.s), "output port 4 of s is not bound to output port 4 of composite node p");
473
474 std::vector< tbb::flow::queue_node<double> > v( 5, tbb::flow::queue_node<double>(g) );
475 tbb::flow::make_edge( tbb::flow::output_port<0>(p), v[0] );
476 tbb::flow::make_edge( tbb::flow::output_port<1>(p), v[1] );
477 tbb::flow::make_edge( tbb::flow::output_port<2>(p), v[2] );
478 tbb::flow::make_edge( tbb::flow::output_port<3>(p), v[3] );
479 tbb::flow::make_edge( tbb::flow::output_port<4>(p), v[4] );
480
481 for( double offset = 1; offset < 10000; offset *= 10 ) {
482 tbb::flow::input_port<0>(p).try_put( offset );
483 tbb::flow::input_port<1>(p).try_put( offset + 1 );
484 tbb::flow::input_port<2>(p).try_put( offset + 2 );
485 tbb::flow::input_port<3>(p).try_put( offset + 3 );
486 tbb::flow::input_port<4>(p).try_put( offset + 4 );
487 }
488 g.wait_for_all();
489
490 double x;
491 while ( v[0].try_get(x) ) {
492 g.wait_for_all();
493 for ( int i = 1; i < 5; ++i ) {
494 v[i].try_get(x);
495 g.wait_for_all();
496 }
497 }
498 return 0;
499}
500
501struct input_only_output_only_seq {
502 int operator()(int i){ return (i + 3) / 4 - 1;}
503};
504
505void input_only_output_only_composite(bool hidden) {
506 tbb::flow::graph g;
507#if TBB_PREVIEW_FLOW_GRAPH_TRACE
508 tbb::flow::composite_node<tbb::flow::tuple<int>, tbb::flow::tuple<int> > input_output(g, "test_name");
509#else
510 tbb::flow::composite_node<tbb::flow::tuple<int>, tbb::flow::tuple<int> > input_output(g);
511#endif
512 typedef tbb::flow::composite_node<tbb::flow::tuple<int>, tbb::flow::tuple<> > input_only_composite;
513 typedef tbb::flow::composite_node<tbb::flow::tuple<>, tbb::flow::tuple<int> > output_only_composite;
514 typedef tbb::flow::source_node<int> src_type;
515 typedef tbb::flow::queue_node<int> q_type;
516 typedef tbb::flow::function_node<int, int> f_type;
517 typedef tbb::flow::sequencer_node<int> sequencer_type;
518
519 int num = 0;
520 int finish=1000;
521 int step = 4;
522
523 input_only_composite a_in(g);
524 output_only_composite a_out(g);
525
526 src_type src(g, src_body(finish, step), false);
527 q_type que(g);
528 f_type f(g, 1, passthru_body());
529
530 // Sequencer_node is needed, because serial function_node guarantees only serial body execution,
531 // not a sequential order of messages dispatch
532 sequencer_type seq(g, input_only_output_only_seq());
533
534 tbb::flow::tuple<f_type& > input_tuple(f);
535 a_in.set_external_ports(input_tuple);
536 ASSERT(&tbb::flow::get<0>(a_in.input_ports()) == &f, "f not bound to input port 0 in composite_node a_in");
537
538 tbb::flow::tuple<src_type&> output_tuple(src);
539 a_out.set_external_ports(output_tuple);
540 ASSERT(&tbb::flow::get<0>(a_out.output_ports()) == &src, "src not bound to output port 0 in composite_node a_out");
541
542 if(hidden) {
543 a_in.add_nodes(f, seq, que);
544 a_out.add_nodes(src);
545 } else {
546 a_in.add_visible_nodes(f, seq, que);
547 a_out.add_visible_nodes(src);
548 }
549
550 tbb::flow::make_edge(a_out, a_in);
551 tbb::flow::make_edge(f, seq);
552 tbb::flow::make_edge(seq, que);
553 src.activate();
554 g.wait_for_all();
555
556 for(int i = 1; i<finish/step; ++i) {
557 que.try_get(num);
558 ASSERT(num == 4*i - 3, "number does not match position in sequence");
559 }
560 g.wait_for_all();
561}
562
563#endif // __TBB_FLOW_GRAPH_CPP11_FEATURES
564
565int TestMain() {
566
567#if __TBB_FLOW_GRAPH_CPP11_FEATURES
568
569 add_all_nodes();
570 test_tiny(false);
571 test_tiny(true);
572 test_adder(false);
573 test_adder(true);
574 test_nested_adder(true);
575 test_nested_adder(false);
576 test_prefix(false);
577 test_prefix(true);
578 input_only_output_only_composite(true);
579 input_only_output_only_composite(false);
580
581 return Harness::Done;
582#else
583 return Harness::Skipped;
584#endif
585
586}
587