1 /*
2     Copyright (c) 2020-2023 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 "conformance_flowgraph.h"
22 #include "common/test_invoke.h"
23 
24 using input_msg = conformance::message</*default_ctor*/true, /*copy_ctor*/true, /*copy_assign*/false>;
25 using output_msg = conformance::message</*default_ctor*/false, /*copy_ctor*/true, /*copy_assign*/false>;
26 
27 //! \file conformance_function_node.cpp
28 //! \brief Test for [flow_graph.function_node] specification
29 
30 /*
31     Test node deduction guides
32 */
33 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
34 
35 int function_body_f(const int&) { return 1; }
36 
37 template <typename Body>
38 void test_deduction_guides_common(Body body) {
39     using namespace tbb::flow;
40     graph g;
41 
42     function_node f1(g, unlimited, body);
43     static_assert(std::is_same_v<decltype(f1), function_node<int, int>>);
44 
45     function_node f2(g, unlimited, body, rejecting());
46     static_assert(std::is_same_v<decltype(f2), function_node<int, int, rejecting>>);
47 
48     function_node f3(g, unlimited, body, node_priority_t(5));
49     static_assert(std::is_same_v<decltype(f3), function_node<int, int>>);
50 
51     function_node f4(g, unlimited, body, rejecting(), node_priority_t(5));
52     static_assert(std::is_same_v<decltype(f4), function_node<int, int, rejecting>>);
53 
54 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
55     function_node f5(follows(f2), unlimited, body);
56     static_assert(std::is_same_v<decltype(f5), function_node<int, int>>);
57 
58     function_node f6(follows(f5), unlimited, body, rejecting());
59     static_assert(std::is_same_v<decltype(f6), function_node<int, int, rejecting>>);
60 
61     function_node f7(follows(f6), unlimited, body, node_priority_t(5));
62     static_assert(std::is_same_v<decltype(f7), function_node<int, int>>);
63 
64     function_node f8(follows(f7), unlimited, body, rejecting(), node_priority_t(5));
65     static_assert(std::is_same_v<decltype(f8), function_node<int, int, rejecting>>);
66 #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
67 
68     function_node f9(f1);
69     static_assert(std::is_same_v<decltype(f9), function_node<int, int>>);
70 }
71 
72 void test_deduction_guides() {
73     test_deduction_guides_common([](const int&)->int { return 1; });
74     test_deduction_guides_common([](const int&) mutable ->int { return 1; });
75     test_deduction_guides_common(function_body_f);
76 }
77 
78 #endif
79 
80 #if __TBB_CPP17_INVOKE_PRESENT
81 
82 template <typename InputType, typename OutputType1, typename OutputType2,
83           typename Body1, typename Body2>
84 void test_fn_invoke_basic(const Body1& body1, const Body2& body2) {
85     using namespace oneapi::tbb::flow;
86 
87     graph g;
88 
89     function_node<InputType, OutputType1> f1(g, unlimited, body1);
90     function_node<OutputType1, OutputType2> f2(g, unlimited, body2);
91     buffer_node<OutputType2> buf(g);
92 
93     make_edge(f1, f2);
94     make_edge(f2, buf);
95 
96     f1.try_put(InputType{OutputType1{1}});
97 
98     g.wait_for_all();
99 
100     std::size_t result = 0;
101     CHECK(buf.try_get(result));
102     CHECK(result == 1);
103     CHECK(!buf.try_get(result));
104 }
105 
106 void test_fn_invoke() {
107     using output_type = test_invoke::SmartID<std::size_t>;
108     using input_type = test_invoke::SmartID<output_type>;
109     // Testing pointer to member function
110     test_fn_invoke_basic<input_type, output_type, std::size_t>(&input_type::get_id, &output_type::get_id);
111     // Testing pointer to member object
112     test_fn_invoke_basic<input_type, output_type, std::size_t>(&input_type::id, &output_type::id);
113 }
114 #endif // __TBB_CPP17_INVOKE_PRESENT
115 
116 //! Test calling function body
117 //! \brief \ref interface \ref requirement
118 TEST_CASE("Test function_node body") {
119     conformance::test_body_exec<oneapi::tbb::flow::function_node<input_msg, output_msg>, input_msg, output_msg>(oneapi::tbb::flow::unlimited);
120 }
121 
122 //! Test function_node constructors
123 //! \brief \ref requirement
124 TEST_CASE("function_node constructors"){
125     using namespace oneapi::tbb::flow;
126     graph g;
127 
128     conformance::counting_functor<int> fun;
129 
130     function_node<int, int> fn1(g, unlimited, fun);
131     function_node<int, int> fn2(g, unlimited, fun, oneapi::tbb::flow::node_priority_t(1));
132 
133     function_node<int, int, lightweight> lw_node1(g, serial, fun, lightweight());
134     function_node<int, int, lightweight> lw_node2(g, serial, fun, lightweight(), oneapi::tbb::flow::node_priority_t(1));
135 }
136 
137 //! The node that is constructed has a reference to the same graph object as src, has a copy of the initial body used by src, and has the same concurrency threshold as src.
138 //! The predecessors and successors of src are not copied.
139 //! \brief \ref requirement
140 TEST_CASE("function_node copy constructor"){
141     conformance::test_copy_ctor<oneapi::tbb::flow::function_node<int, int>>();
142 }
143 
144 //! Test node reject the incoming message if the concurrency limit achieved.
145 //! \brief \ref interface
146 TEST_CASE("function_node with rejecting policy"){
147     conformance::test_rejecting<oneapi::tbb::flow::function_node<int, int, oneapi::tbb::flow::rejecting>>();
148 }
149 
150 //! Test body copying and copy_body logic
151 //! Test the body object passed to a node is copied
152 //! \brief \ref interface
153 TEST_CASE("function_node and body copying"){
154     conformance::test_copy_body_function<oneapi::tbb::flow::function_node<int, int>, conformance::copy_counting_object<int>>(oneapi::tbb::flow::unlimited);
155 }
156 
157 //! Test function_node is a graph_node, receiver<Input>, and sender<Output>
158 //! \brief \ref interface
159 TEST_CASE("function_node superclasses"){
160     conformance::test_inheritance<oneapi::tbb::flow::function_node<int, int>, int, int>();
161     conformance::test_inheritance<oneapi::tbb::flow::function_node<void*, float>, void*, float>();
162     conformance::test_inheritance<oneapi::tbb::flow::function_node<input_msg, output_msg>, input_msg, output_msg>();
163 }
164 
165 //! Test node not buffered unsuccessful message, and try_get after rejection should not succeed.
166 //! \brief \ref requirement
167 TEST_CASE("function_node buffering"){
168     conformance::dummy_functor<int> fun;
169     conformance::test_buffering<oneapi::tbb::flow::function_node<input_msg, int, oneapi::tbb::flow::rejecting>, input_msg>(oneapi::tbb::flow::unlimited, fun);
170     conformance::test_buffering<oneapi::tbb::flow::function_node<input_msg, int, oneapi::tbb::flow::queueing>, input_msg>(oneapi::tbb::flow::unlimited, fun);
171 }
172 
173 //! Test node broadcast messages to successors
174 //! \brief \ref requirement
175 TEST_CASE("function_node broadcast"){
176     conformance::counting_functor<int> fun(conformance::expected);
177     conformance::test_forwarding<oneapi::tbb::flow::function_node<input_msg, int>, input_msg, int>(1, oneapi::tbb::flow::unlimited, fun);
178 }
179 
180 //! Test deduction guides
181 //! \brief \ref interface \ref requirement
182 TEST_CASE("Deduction guides"){
183 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
184     test_deduction_guides();
185 #endif
186 }
187 
188 //! Test priorities work in single-threaded configuration
189 //! \brief \ref requirement
190 TEST_CASE("function_node priority support"){
191     conformance::test_priority<oneapi::tbb::flow::function_node<input_msg, int>, input_msg>(oneapi::tbb::flow::unlimited);
192 }
193 
194 //! Test function_node has a user-settable concurrency limit. It can be set to one of predefined values.
195 //! The user can also provide a value of type std::size_t to limit concurrency.
196 //! Test that not more than limited threads works in parallel.
197 //! \brief \ref requirement
198 TEST_CASE("concurrency follows set limits"){
199     conformance::test_concurrency<oneapi::tbb::flow::function_node<int, int>>();
200 }
201 
202 //! Test node Input class meet the DefaultConstructible and CopyConstructible requirements and Output class meet the CopyConstructible requirements.
203 //! \brief \ref interface \ref requirement
204 TEST_CASE("Test function_node Output and Input class") {
205     using Body = conformance::copy_counting_object<int>;
206     conformance::test_output_input_class<oneapi::tbb::flow::function_node<Body, Body>, Body>();
207 }
208 
209 #if __TBB_CPP17_INVOKE_PRESENT
210 //! Test that function_node uses std::invoke to execute the body
211 //! \brief \ref interface \ref requirement
212 TEST_CASE("Test function_node and std::invoke") {
213     test_fn_invoke();
214 }
215 #endif
216