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_INPUT_NODE
22
23 #include "conformance_flowgraph.h"
24
25 //! \file conformance_input_node.cpp
26 //! \brief Test for [flow_graph.input_node] specification
27
28 using output_msg = conformance::message<true, true, true>;
29
30 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
input_body_f(tbb::flow_control &)31 int input_body_f(tbb::flow_control&) { return 42; }
32
test_deduction_guides()33 void test_deduction_guides() {
34 using namespace tbb::flow;
35 graph g;
36
37 auto lambda = [](tbb::flow_control&) { return 42; };
38 auto non_const_lambda = [](tbb::flow_control&) mutable { return 42; };
39
40 // Tests for input_node(graph&, Body)
41 input_node s1(g, lambda);
42 static_assert(std::is_same_v<decltype(s1), input_node<int>>);
43
44 input_node s2(g, non_const_lambda);
45 static_assert(std::is_same_v<decltype(s2), input_node<int>>);
46
47 input_node s3(g, input_body_f);
48 static_assert(std::is_same_v<decltype(s3), input_node<int>>);
49
50 input_node s4(s3);
51 static_assert(std::is_same_v<decltype(s4), input_node<int>>);
52
53 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
54 broadcast_node<int> bc(g);
55
56 // Tests for input_node(const node_set<Args...>&, Body)
57 input_node s5(precedes(bc), lambda);
58 static_assert(std::is_same_v<decltype(s5), input_node<int>>);
59
60 input_node s6(precedes(bc), non_const_lambda);
61 static_assert(std::is_same_v<decltype(s6), input_node<int>>);
62
63 input_node s7(precedes(bc), input_body_f);
64 static_assert(std::is_same_v<decltype(s7), input_node<int>>);
65 #endif
66 g.wait_for_all();
67 }
68
69 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
70
71 template<typename O>
test_inheritance()72 void test_inheritance(){
73 using namespace oneapi::tbb::flow;
74
75 CHECK_MESSAGE((std::is_base_of<graph_node, input_node<O>>::value), "input_node should be derived from graph_node");
76 CHECK_MESSAGE((std::is_base_of<sender<O>, input_node<O>>::value), "input_node should be derived from sender<Output>");
77 CHECK_MESSAGE((!std::is_base_of<receiver<O>, input_node<O>>::value), "input_node cannot have predecessors");
78 }
79
80 //! Test the body object passed to a node is copied
81 //! \brief \ref interface
82 TEST_CASE("input_node and body copying"){
83 conformance::test_copy_body_function<oneapi::tbb::flow::input_node<int>, conformance::copy_counting_object<int>>();
84 }
85
86 //! The node that is constructed has a reference to the same graph object as src,
87 //! has a copy of the initial body used by src.
88 //! The successors of src are not copied.
89 //! \brief \ref requirement
90 TEST_CASE("input_node copy constructor"){
91 using namespace oneapi::tbb::flow;
92 graph g;
93
94 conformance::copy_counting_object<output_msg> fun2;
95
96 input_node<output_msg> node1(g, fun2);
97 conformance::test_push_receiver<output_msg> node2(g);
98 conformance::test_push_receiver<output_msg> node3(g);
99
100 oneapi::tbb::flow::make_edge(node1, node2);
101
102 input_node<output_msg> node_copy(node1);
103
104 conformance::copy_counting_object<output_msg> b2 = copy_body<conformance::copy_counting_object<output_msg>, input_node<output_msg>>(node_copy);
105
106 CHECK_MESSAGE((fun2.copy_count + 1 < b2.copy_count), "constructor should copy bodies");
107
108 oneapi::tbb::flow::make_edge(node_copy, node3);
109
110 node_copy.activate();
111 g.wait_for_all();
112
113 CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && conformance::get_values(node3).size() == 1), "Copied node doesn`t copy successor");
114
115 node1.activate();
116 g.wait_for_all();
117
118 CHECK_MESSAGE((conformance::get_values(node2).size() == 1 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy successor");
119 }
120
121 //! Test inheritance relations
122 //! \brief \ref interface
123 TEST_CASE("input_node superclasses"){
124 test_inheritance<int>();
125 test_inheritance<void*>();
126 test_inheritance<output_msg>();
127 }
128
129 //! Test input_node forwarding
130 //! \brief \ref requirement
131 TEST_CASE("input_node forwarding"){
132 conformance::counting_functor<output_msg> fun(conformance::expected);
133 conformance::test_forwarding<oneapi::tbb::flow::input_node<output_msg>, void, output_msg>(5, fun);
134 }
135
136 //! Test input_node buffering
137 //! \brief \ref requirement
138 TEST_CASE("input_node buffering"){
139 conformance::dummy_functor<int> fun;
140 conformance::test_buffering<oneapi::tbb::flow::input_node<int>, int>(fun);
141 }
142
143 //! Test calling input_node body
144 //! \brief \ref interface \ref requirement
145 TEST_CASE("input_node body") {
146 oneapi::tbb::flow::graph g;
147 constexpr std::size_t counting_threshold = 10;
148 conformance::counting_functor<output_msg> fun(counting_threshold);
149
150 oneapi::tbb::flow::input_node<output_msg> node1(g, fun);
151 conformance::test_push_receiver<output_msg> node2(g);
152
153 oneapi::tbb::flow::make_edge(node1, node2);
154
155 node1.activate();
156 g.wait_for_all();
157
158 CHECK_MESSAGE((conformance::get_values(node2).size() == counting_threshold), "Descendant of the node needs to be receive N messages");
159 CHECK_MESSAGE((fun.execute_count == counting_threshold + 1), "Body of the node needs to be executed N + 1 times");
160 }
161
162 //! Test deduction guides
163 //! \brief \ref interface \ref requirement
164 TEST_CASE("Deduction guides"){
165 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
166 test_deduction_guides();
167 #endif
168 }
169
170 //! Test that measured concurrency respects set limits
171 //! \brief \ref requirement
172 TEST_CASE("concurrency follows set limits"){
173 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
174 oneapi::tbb::this_task_arena::max_concurrency());
175
176
177 utils::ConcurrencyTracker::Reset();
178 oneapi::tbb::flow::graph g;
179 conformance::concurrency_peak_checker_body counter(1);
180 oneapi::tbb::flow::input_node<int> testing_node(g, counter);
181
182 conformance::test_push_receiver<int> sink(g);
183
184 make_edge(testing_node, sink);
185 testing_node.activate();
186
187 g.wait_for_all();
188 }
189
190 //! Test node Output class meet the CopyConstructible requirements.
191 //! \brief \ref interface \ref requirement
192 TEST_CASE("Test input_node Output class") {
193 conformance::test_output_class<oneapi::tbb::flow::input_node<conformance::copy_counting_object<int>>>();
194 }
195
196 struct input_node_counter{
197 static int count;
198 int N;
input_node_counterinput_node_counter199 input_node_counter(int n) : N(n){};
200
operator ()input_node_counter201 int operator()( oneapi::tbb::flow_control & fc ) {
202 ++count;
203 if(count > N){
204 fc.stop();
205 return N;
206 }
207 return N;
208 }
209 };
210
211 struct function_node_counter{
212 static int count;
213
operator ()function_node_counter214 int operator()( int ) {
215 ++count;
216 utils::doDummyWork(1000000);
217 CHECK_MESSAGE((input_node_counter::count <= function_node_counter::count + 1), "input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty");
218 return 1;
219 }
220 };
221
222 int input_node_counter::count = 0;
223 int function_node_counter::count = 0;
224
225 //! Test input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty.
226 //! \brief \ref requirement
227 TEST_CASE("input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty.") {
228 oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, 1);
229 oneapi::tbb::flow::graph g;
230 input_node_counter fun1(500);
231 function_node_counter fun2;
232
233 oneapi::tbb::flow::function_node <int, int, oneapi::tbb::flow::rejecting> fnode(g, oneapi::tbb::flow::serial, fun2);
234 oneapi::tbb::flow::input_node<int> testing_node(g, fun1);
235
236 make_edge(testing_node, fnode);
237 testing_node.activate();
238
239 g.wait_for_all();
240 }
241