1 /*
2     Copyright (c) 2020-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #if __INTEL_COMPILER && _MSC_VER
18 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19 #endif
20 
21 #define CONFORMANCE_CONTINUE_NODE
22 
23 #include "conformance_flowgraph.h"
24 
25 //! \file conformance_continue_node.cpp
26 //! \brief Test for [flow_graph.continue_node] specification
27 
28 using output_msg = conformance::message</*default_ctor*/false, /*copy_ctor*/true, /*copy_assign*/true/*enable for queue_node successor*/>;
29 
30 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
31 
32 template <typename ExpectedType, typename Body>
test_deduction_guides_common(Body body)33 void test_deduction_guides_common(Body body) {
34     using namespace tbb::flow;
35     graph g;
36 
37     continue_node c1(g, body);
38     static_assert(std::is_same_v<decltype(c1), continue_node<ExpectedType>>);
39 
40     continue_node c2(g, body, lightweight());
41     static_assert(std::is_same_v<decltype(c2), continue_node<ExpectedType, lightweight>>);
42 
43     continue_node c3(g, 5, body);
44     static_assert(std::is_same_v<decltype(c3), continue_node<ExpectedType>>);
45 
46     continue_node c4(g, 5, body, lightweight());
47     static_assert(std::is_same_v<decltype(c4), continue_node<ExpectedType, lightweight>>);
48 
49     continue_node c5(g, body, node_priority_t(5));
50     static_assert(std::is_same_v<decltype(c5), continue_node<ExpectedType>>);
51 
52     continue_node c6(g, body, lightweight(), node_priority_t(5));
53     static_assert(std::is_same_v<decltype(c6), continue_node<ExpectedType, lightweight>>);
54 
55     continue_node c7(g, 5, body, node_priority_t(5));
56     static_assert(std::is_same_v<decltype(c7), continue_node<ExpectedType>>);
57 
58     continue_node c8(g, 5, body, lightweight(), node_priority_t(5));
59     static_assert(std::is_same_v<decltype(c8), continue_node<ExpectedType, lightweight>>);
60 
61 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
62     broadcast_node<continue_msg> b(g);
63 
64     continue_node c9(follows(b), body);
65     static_assert(std::is_same_v<decltype(c9), continue_node<ExpectedType>>);
66 
67     continue_node c10(follows(b), body, lightweight());
68     static_assert(std::is_same_v<decltype(c10), continue_node<ExpectedType, lightweight>>);
69 
70     continue_node c11(follows(b), 5, body);
71     static_assert(std::is_same_v<decltype(c11), continue_node<ExpectedType>>);
72 
73     continue_node c12(follows(b), 5, body, lightweight());
74     static_assert(std::is_same_v<decltype(c12), continue_node<ExpectedType, lightweight>>);
75 
76     continue_node c13(follows(b), body, node_priority_t(5));
77     static_assert(std::is_same_v<decltype(c13), continue_node<ExpectedType>>);
78 
79     continue_node c14(follows(b), body, lightweight(), node_priority_t(5));
80     static_assert(std::is_same_v<decltype(c14), continue_node<ExpectedType, lightweight>>);
81 
82     continue_node c15(follows(b), 5, body, node_priority_t(5));
83     static_assert(std::is_same_v<decltype(c15), continue_node<ExpectedType>>);
84 
85     continue_node c16(follows(b), 5, body, lightweight(), node_priority_t(5));
86     static_assert(std::is_same_v<decltype(c16), continue_node<ExpectedType, lightweight>>);
87 #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
88 
89     continue_node c17(c1);
90     static_assert(std::is_same_v<decltype(c17), continue_node<ExpectedType>>);
91 }
92 
continue_body_f(const tbb::flow::continue_msg &)93 int continue_body_f(const tbb::flow::continue_msg&) { return 1; }
continue_void_body_f(const tbb::flow::continue_msg &)94 void continue_void_body_f(const tbb::flow::continue_msg&) {}
95 
test_deduction_guides()96 void test_deduction_guides() {
97     using tbb::flow::continue_msg;
98     test_deduction_guides_common<int>([](const continue_msg&)->int { return 1; } );
99     test_deduction_guides_common<continue_msg>([](const continue_msg&) {});
100 
101     test_deduction_guides_common<int>([](const continue_msg&) mutable ->int { return 1; });
102     test_deduction_guides_common<continue_msg>([](const continue_msg&) mutable {});
103 
104     test_deduction_guides_common<int>(continue_body_f);
105     test_deduction_guides_common<continue_msg>(continue_void_body_f);
106 }
107 
108 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
109 
110 //! Test execution of node body
111 //! Test node can do try_put call
112 //! \brief \ref interface \ref requirement
113 TEST_CASE("continue body") {
114     conformance::test_body_exec<oneapi::tbb::flow::continue_node<output_msg>, oneapi::tbb::flow::continue_msg, output_msg>();
115 }
116 
117 //! Test continue_node is a graph_node, receiver<continue_msg>, and sender<Output>
118 //! \brief \ref interface
119 TEST_CASE("continue_node superclasses"){
120     conformance::test_inheritance<oneapi::tbb::flow::continue_node<int>, oneapi::tbb::flow::continue_msg, int>();
121     conformance::test_inheritance<oneapi::tbb::flow::continue_node<void*>, oneapi::tbb::flow::continue_msg, void*>();
122     conformance::test_inheritance<oneapi::tbb::flow::continue_node<output_msg>, oneapi::tbb::flow::continue_msg, output_msg>();
123 }
124 
125 //! Test body copying and copy_body logic
126 //! Test the body object passed to a node is copied
127 //! \brief \ref interface
128 TEST_CASE("continue_node and body copying"){
129     conformance::test_copy_body_function<oneapi::tbb::flow::continue_node<int>, conformance::copy_counting_object<int, oneapi::tbb::flow::continue_msg>>();
130 }
131 
132 //! Test deduction guides
133 //! \brief \ref interface \ref requirement
134 TEST_CASE("Deduction guides"){
135 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
136     test_deduction_guides();
137 #endif
138 }
139 
140 //! Test node broadcast messages to successors
141 //! \brief \ref requirement
142 TEST_CASE("continue_node broadcast"){
143     conformance::counting_functor<int> fun(conformance::expected);
144     conformance::test_forwarding<oneapi::tbb::flow::continue_node<int>, oneapi::tbb::flow::continue_msg, int>(1, fun);
145 }
146 
147 //! Test node not buffered unsuccessful message, and try_get after rejection should not succeed.
148 //! \brief \ref requirement
149 TEST_CASE("continue_node buffering"){
150     conformance::dummy_functor<int> fun;
151     conformance::test_buffering<oneapi::tbb::flow::continue_node<int>, oneapi::tbb::flow::continue_msg>(fun);
152 }
153 
154 //! Test all node costructors
155 //! \brief \ref requirement
156 TEST_CASE("continue_node constructors"){
157     using namespace oneapi::tbb::flow;
158     graph g;
159 
160     conformance::counting_functor<int> fun;
161 
162     continue_node<int> proto1(g, fun);
163     continue_node<int> proto2(g, fun, oneapi::tbb::flow::node_priority_t(1));
164     continue_node<int> proto3(g, 2, fun);
165     continue_node<int> proto4(g, 2, fun, oneapi::tbb::flow::node_priority_t(1));
166 
167     continue_node<int, lightweight> lw_node1(g, fun, lightweight());
168     continue_node<int, lightweight> lw_node2(g, fun, lightweight(), oneapi::tbb::flow::node_priority_t(1));
169     continue_node<int, lightweight> lw_node3(g, 2, fun, lightweight());
170     continue_node<int, lightweight> lw_node4(g, 2, fun, lightweight(), oneapi::tbb::flow::node_priority_t(1));
171 }
172 
173 //! The node that is constructed has a reference to the same graph object as src,
174 //! has a copy of the initial body used by src, and only has a non-zero threshold if src is constructed with a non-zero threshold..
175 //! The predecessors and successors of src are not copied.
176 //! \brief \ref interface
177 TEST_CASE("continue_node copy constructor"){
178     using namespace oneapi::tbb::flow;
179     graph g;
180 
181     conformance::dummy_functor<oneapi::tbb::flow::continue_msg> fun1;
182     using counting_body = conformance::copy_counting_object<output_msg, oneapi::tbb::flow::continue_msg>;
183     counting_body fun2;
184 
185     continue_node<oneapi::tbb::flow::continue_msg> node0(g, fun1);
186     continue_node<output_msg> node1(g, 2, fun2);
187     conformance::test_push_receiver<output_msg> node2(g);
188     conformance::test_push_receiver<output_msg> node3(g);
189 
190     oneapi::tbb::flow::make_edge(node0, node1);
191     oneapi::tbb::flow::make_edge(node1, node2);
192 
193     continue_node<output_msg> node_copy(node1);
194 
195     counting_body b2 = copy_body<counting_body, continue_node<output_msg>>(node_copy);
196 
197     CHECK_MESSAGE((fun2.copy_count + 1 < b2.copy_count), "constructor should copy bodies");
198 
199     oneapi::tbb::flow::make_edge(node_copy, node3);
200 
201     node_copy.try_put(oneapi::tbb::flow::continue_msg());
202     g.wait_for_all();
203 
204     CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy successor, but copy number of predecessors");
205 
206     node_copy.try_put(oneapi::tbb::flow::continue_msg());
207     g.wait_for_all();
208 
209     CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && conformance::get_values(node3).size() == 1), "Copied node doesn`t copy successor, but copy number of predecessors");
210 
211     node1.try_put(oneapi::tbb::flow::continue_msg());
212     node1.try_put(oneapi::tbb::flow::continue_msg());
213     node0.try_put(oneapi::tbb::flow::continue_msg());
214     g.wait_for_all();
215 
216     CHECK_MESSAGE((conformance::get_values(node2).size() == 1 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy predecessor, but copy number of predecessors");
217 }
218 
219 //! Test continue_node wait for their predecessors to complete before executing, but no explicit data is passed across the incoming edges.
220 //! \brief \ref requirement
221 TEST_CASE("continue_node number_of_predecessors") {
222     oneapi::tbb::flow::graph g;
223 
224     conformance::counting_functor<int> fun;
225 
226     oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg> node1(g, fun);
227     oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg> node2(g, 1, fun);
228     oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg> node3(g, 1, fun);
229     oneapi::tbb::flow::continue_node<int> node4(g, fun);
230 
231     oneapi::tbb::flow::make_edge(node1, node2);
232     oneapi::tbb::flow::make_edge(node2, node4);
233     oneapi::tbb::flow::make_edge(node1, node3);
234     oneapi::tbb::flow::make_edge(node1, node3);
235     oneapi::tbb::flow::remove_edge(node1, node3);
236     oneapi::tbb::flow::make_edge(node3, node4);
237     node3.try_put(oneapi::tbb::flow::continue_msg());
238     node2.try_put(oneapi::tbb::flow::continue_msg());
239     node1.try_put(oneapi::tbb::flow::continue_msg());
240 
241     g.wait_for_all();
242     CHECK_MESSAGE((fun.execute_count == 4), "Node wait for their predecessors to complete before executing");
243 }
244 
245 //! Test nodes for execution with priority in single-threaded configuration
246 //! \brief \ref requirement
247 TEST_CASE("continue_node priority support"){
248     conformance::test_priority<oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg, int>, oneapi::tbb::flow::continue_msg>();
249 }
250 
251 //! Test node Output class meet the CopyConstructible requirements.
252 //! \brief \ref requirement
253 TEST_CASE("continue_node Output class") {
254     conformance::test_output_class<oneapi::tbb::flow::continue_node<conformance::copy_counting_object<int, oneapi::tbb::flow::continue_msg>>, conformance::copy_counting_object<int, oneapi::tbb::flow::continue_msg>>();
255 }
256 
257 //! Test body `try_put' statement not wait for the execution of the body to complete
258 //! \brief \ref requirement
259 TEST_CASE("continue_node `try_put' statement not wait for the execution of the body to complete") {
260     conformance::wait_flag_body body;
261     oneapi::tbb::flow::graph g;
262 
263     oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg> node1(g, body);
264     node1.try_put(oneapi::tbb::flow::continue_msg());
265     conformance::wait_flag_body::flag.store(true);
266     g.wait_for_all();
267 }
268