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 #include "common/test.h"
22 
23 #include "common/utils.h"
24 #include "common/graph_utils.h"
25 
26 #include "oneapi/tbb/flow_graph.h"
27 #include "oneapi/tbb/task_arena.h"
28 
29 #include "oneapi/tbb/global_control.h"
30 #include "conformance_flowgraph.h"
31 
32 //! \file conformance_continue_node.cpp
33 //! \brief Test for [flow_graph.continue_node] specification
34 
35 /*
36 TODO: implement missing conformance tests for continue_node:
37   - [ ] For `test_forwarding' check that the value passed is the actual one received.
38   - [ ] The `copy_body' function copies altered body (e.g. after its successful invocation).
39   - [ ] Improve CTAD test.
40   - [ ] Improve constructors test, including addition of calls to constructors with
41     `number_of_predecessors' parameter.
42   - [ ] Explicit test for copy constructor of the node.
43   - [ ] Rewrite test_priority.
44   - [ ] Check `Output' type indeed copy-constructed and copy-assigned while working with the node.
45   - [ ] Explicit test for correct working of `number_of_predecessors' constructor parameter,
46     including taking it into account when making and removing edges.
47   - [ ] Add testing of `try_put' statement. In particular that it does not wait for the execution of
48     the body to complete.
49 */
50 
51 void test_cont_body(){
52     oneapi::tbb::flow::graph g;
53     inc_functor<int> cf;
54     cf.execute_count = 0;
55 
56     oneapi::tbb::flow::continue_node<int> node1(g, cf);
57 
58     const size_t n = 10;
59     for(size_t i = 0; i < n; ++i) {
60         CHECK_MESSAGE((node1.try_put(oneapi::tbb::flow::continue_msg()) == true),
61                       "continue_node::try_put() should never reject a message.");
62     }
63     g.wait_for_all();
64 
65     CHECK_MESSAGE( (cf.execute_count == n), "Body of the first node needs to be executed N times");
66 }
67 
68 template<typename O>
69 void test_inheritance(){
70     using namespace oneapi::tbb::flow;
71 
72     CHECK_MESSAGE( (std::is_base_of<graph_node, continue_node<O>>::value), "continue_node should be derived from graph_node");
73     CHECK_MESSAGE( (std::is_base_of<receiver<continue_msg>, continue_node<O>>::value), "continue_node should be derived from receiver<Input>");
74     CHECK_MESSAGE( (std::is_base_of<sender<O>, continue_node<O>>::value), "continue_node should be derived from sender<Output>");
75 }
76 
77 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
78 void test_deduction_guides(){
79     oneapi::tbb::flow::graph g;
80     inc_functor<int> fun;
81     oneapi::tbb::flow::continue_node node1(g, fun);
82 }
83 #endif
84 
85 void test_forwarding(){
86     oneapi::tbb::flow::graph g;
87     inc_functor<int> fun;
88     fun.execute_count = 0;
89 
90     oneapi::tbb::flow::continue_node<int> node1(g, fun);
91     test_push_receiver<int> node2(g);
92     test_push_receiver<int> node3(g);
93 
94     oneapi::tbb::flow::make_edge(node1, node2);
95     oneapi::tbb::flow::make_edge(node1, node3);
96 
97     node1.try_put(oneapi::tbb::flow::continue_msg());
98     g.wait_for_all();
99 
100     CHECK_MESSAGE( (get_count(node2) == 1), "Descendant of the node must receive one message.");
101     CHECK_MESSAGE( (get_count(node3) == 1), "Descendant of the node must receive one message.");
102 }
103 
104 void test_buffering(){
105     oneapi::tbb::flow::graph g;
106     inc_functor<int> fun;
107 
108     oneapi::tbb::flow::continue_node<int> node(g, fun);
109     oneapi::tbb::flow::limiter_node<int> rejecter(g, 0);
110 
111     oneapi::tbb::flow::make_edge(node, rejecter);
112     node.try_put(oneapi::tbb::flow::continue_msg());
113 
114     int tmp = -1;
115     CHECK_MESSAGE( (node.try_get(tmp) == false), "try_get after rejection should not succeed");
116     CHECK_MESSAGE( (tmp == -1), "try_get after rejection should not alter passed value");
117     g.wait_for_all();
118 }
119 
120 void test_policy_ctors(){
121     using namespace oneapi::tbb::flow;
122     graph g;
123 
124     inc_functor<int> fun;
125 
126     continue_node<int, lightweight> lw_node(g, fun);
127 }
128 
129 void test_ctors(){
130     using namespace oneapi::tbb::flow;
131     graph g;
132 
133     inc_functor<int> fun;
134 
135     continue_node<int> proto1(g, 2, fun, oneapi::tbb::flow::node_priority_t(1));
136 }
137 
138 template<typename O>
139 struct CopyCounterBody{
140     size_t copy_count;
141 
142     CopyCounterBody():
143         copy_count(0) {}
144 
145     CopyCounterBody(const CopyCounterBody<O>& other):
146         copy_count(other.copy_count + 1) {}
147 
148     CopyCounterBody& operator=(const CopyCounterBody<O>& other){
149         copy_count = other.copy_count + 1;
150         return *this;
151     }
152 
153     O operator()(oneapi::tbb::flow::continue_msg){
154         return 1;
155     }
156 };
157 
158 void test_copies(){
159     using namespace oneapi::tbb::flow;
160 
161     CopyCounterBody<int> b;
162 
163     graph g;
164     continue_node<int> fn(g, b);
165 
166     CopyCounterBody<int> b2 = copy_body<CopyCounterBody<int>,
167                                              continue_node<int>>(fn);
168 
169     CHECK_MESSAGE( (b.copy_count + 2 <= b2.copy_count), "copy_body and constructor should copy bodies");
170 }
171 
172 
173 void test_priority(){
174     size_t concurrency_limit = 1;
175     oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_limit);
176 
177     oneapi::tbb::flow::graph g;
178 
179     oneapi::tbb::flow::continue_node<oneapi::tbb::flow::continue_msg> source(g,
180                                                              [](oneapi::tbb::flow::continue_msg){ return oneapi::tbb::flow::continue_msg();});
181     source.try_put(oneapi::tbb::flow::continue_msg());
182 
183     first_functor<int>::first_id = -1;
184     first_functor<int> low_functor(1);
185     first_functor<int> high_functor(2);
186 
187     oneapi::tbb::flow::continue_node<int, int> high(g, high_functor, oneapi::tbb::flow::node_priority_t(1));
188     oneapi::tbb::flow::continue_node<int, int> low(g, low_functor);
189 
190     make_edge(source, low);
191     make_edge(source, high);
192 
193     g.wait_for_all();
194 
195     CHECK_MESSAGE( (first_functor<int>::first_id == 2), "High priority node should execute first");
196 }
197 
198 //! Test node costructors
199 //! \brief \ref requirement
200 TEST_CASE("continue_node constructors"){
201     test_ctors();
202 }
203 
204 //! Test priorities work in single-threaded configuration
205 //! \brief \ref requirement
206 TEST_CASE("continue_node priority support"){
207     test_priority();
208 }
209 
210 //! Test body copying and copy_body logic
211 //! \brief \ref interface
212 TEST_CASE("continue_node and body copying"){
213     test_copies();
214 }
215 
216 //! Test constructors
217 //! \brief \ref interface
218 TEST_CASE("continue_node constructors"){
219     test_policy_ctors();
220 }
221 
222 //! Test continue_node buffering
223 //! \brief \ref requirement
224 TEST_CASE("continue_node buffering"){
225     test_buffering();
226 }
227 
228 //! Test function_node broadcasting
229 //! \brief \ref requirement
230 TEST_CASE("continue_node broadcast"){
231     test_forwarding();
232 }
233 
234 //! Test deduction guides
235 //! \brief \ref interface \ref requirement
236 TEST_CASE("Deduction guides"){
237 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
238     test_deduction_guides();
239 #endif
240 }
241 
242 //! Test inheritance relations
243 //! \brief \ref interface
244 TEST_CASE("continue_node superclasses"){
245     test_inheritance<int>();
246     test_inheritance<void*>();
247 }
248 
249 //! Test body execution
250 //! \brief \ref interface \ref requirement
251 TEST_CASE("continue body") {
252     test_cont_body();
253 }
254