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 #define CONFORMANCE_MULTIFUNCTION_NODE
22
23 #include "conformance_flowgraph.h"
24 #include "common/test_invoke.h"
25
26 //! \file conformance_multifunction_node.cpp
27 //! \brief Test for [flow_graph.function_node] specification
28
29 using input_msg = conformance::message</*default_ctor*/true, /*copy_ctor*/true, /*copy_assign*/false>;
30 using output_msg = conformance::message</*default_ctor*/false, /*copy_ctor*/false, /*copy_assign*/false>;
31
32 /*
33 Test function_node is a graph_node, receiver<Input>, and sender<Output>
34 */
35 template<typename I, typename O>
test_inheritance()36 void test_inheritance(){
37 using namespace oneapi::tbb::flow;
38
39 CHECK_MESSAGE((std::is_base_of<graph_node, multifunction_node<I, O>>::value), "multifunction_node should be derived from graph_node");
40 CHECK_MESSAGE((std::is_base_of<receiver<I>, multifunction_node<I, O>>::value), "multifunction_node should be derived from receiver<Input>");
41 }
42
43 //! Test node reject the incoming message if the concurrency limit achieved.
44 //! \brief \ref interface
45 TEST_CASE("multifunction_node with rejecting policy"){
46 conformance::test_rejecting<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>, oneapi::tbb::flow::rejecting>>();
47 }
48
49 //! Test nodes for execution with priority in single-threaded configuration
50 //! \brief \ref interface
51 TEST_CASE("multifunction_node priority"){
52 conformance::test_priority<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>>, input_msg>(oneapi::tbb::flow::unlimited);
53 }
54
55 //! Test function_node has a user-settable concurrency limit. It can be set to one of predefined values.
56 //! The user can also provide a value of type std::size_t to limit concurrency.
57 //! Test that not more than limited threads works in parallel.
58 //! \brief \ref interface
59 TEST_CASE("multifunction_node concurrency"){
60 conformance::test_concurrency<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>>();
61 }
62
63 //! Test all node constructors
64 //! \brief \ref interface
65 TEST_CASE("multifunction_node constructors"){
66 using namespace oneapi::tbb::flow;
67 graph g;
68
69 conformance::counting_functor<int> fun;
70
71 multifunction_node<int, std::tuple<int>> fn1(g, unlimited, fun);
72 multifunction_node<int, std::tuple<int>> fn2(g, unlimited, fun, oneapi::tbb::flow::node_priority_t(1));
73
74 multifunction_node<int, std::tuple<int>, lightweight> lw_node1(g, serial, fun, lightweight());
75 multifunction_node<int, std::tuple<int>, lightweight> lw_node2(g, serial, fun, lightweight(), oneapi::tbb::flow::node_priority_t(1));
76 }
77
78 //! 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.
79 //! The predecessors and successors of src are not copied.
80 //! \brief \ref interface
81 TEST_CASE("multifunction_node copy constructor"){
82 conformance::test_copy_ctor<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>>();
83 }
84
85 //! Test node not buffered unsuccessful message, and try_get after rejection should not succeed.
86 //! \brief \ref requirement
87 TEST_CASE("multifunction_node buffering"){
88 conformance::dummy_functor<int> fun;
89 conformance::test_buffering<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>,
90 oneapi::tbb::flow::rejecting>, input_msg>(oneapi::tbb::flow::unlimited, fun);
91 }
92
93 //! Test multifunction_node broadcasting
94 //! \brief \ref requirement
95 TEST_CASE("multifunction_node broadcast"){
96 conformance::counting_functor<int> fun(conformance::expected);
97 conformance::test_forwarding<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>>, input_msg, int>(1, oneapi::tbb::flow::unlimited, fun);
98 }
99
100 //! Test the body object passed to a node is copied
101 //! \brief \ref interface
102 TEST_CASE("multifunction_node copy body"){
103 conformance::test_copy_body_function<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>, conformance::copy_counting_object<int>>(oneapi::tbb::flow::unlimited);
104 }
105
106 //! Test execution of node body
107 //! Test node can do try_put call
108 //! \brief \ref interface \ref requirement
109 TEST_CASE("multifunction_node body") {
110 conformance::test_body_exec<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<output_msg>, oneapi::tbb::flow::rejecting>, input_msg, output_msg>(oneapi::tbb::flow::unlimited);
111 }
112
113 //! Test multifunction_node output_ports() returns a tuple of output ports.
114 //! \brief \ref interface \ref requirement
115 TEST_CASE("multifunction_node output_ports") {
116 using namespace oneapi::tbb::flow;
117 graph g;
118 conformance::dummy_functor<int> fun;
119 oneapi::tbb::flow::multifunction_node <int, std::tuple<int>> node(g, unlimited, fun);
120
121 CHECK_MESSAGE((std::is_same<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>::output_ports_type&,
122 decltype(node.output_ports())>::value), "multifunction_node output_ports should returns a tuple of output ports");
123 }
124
125 //! Test inheritance relations
126 //! \brief \ref interface
127 TEST_CASE("multifunction_node superclasses"){
128 test_inheritance<int, std::tuple<int>>();
129 test_inheritance<void*, std::tuple<float>>();
130 test_inheritance<input_msg, std::tuple<output_msg>>();
131 }
132
133 //! Test node Input class meet the DefaultConstructible and CopyConstructible requirements and Output class meet the CopyConstructible requirements.
134 //! \brief \ref interface \ref requirement
135 TEST_CASE("Test function_node Output and Input class") {
136 using Body = conformance::copy_counting_object<int>;
137 conformance::test_output_input_class<oneapi::tbb::flow::multifunction_node<Body, std::tuple<Body>>, Body>();
138 }
139
140 #if __TBB_CPP17_INVOKE_PRESENT
141 //! Test that multifunction_node uses std::invoke to execute the body
142 //! \brief \ref interface \ref requirement
143 TEST_CASE("Test multifunction_node and std::invoke") {
144 using namespace oneapi::tbb::flow;
145
146 using output_type1 = test_invoke::SmartID<std::size_t>;
147 using input_type = test_invoke::SmartID<output_type1>;
148
149 using output_tuple1 = std::tuple<output_type1, output_type1>;
150 using output_tuple2 = std::tuple<std::size_t>;
151
152 using first_mf_node_type = multifunction_node<input_type, output_tuple1>;
153 using second_mf_node_type = multifunction_node<output_type1, output_tuple2>;
154
155 using first_ports_type = typename first_mf_node_type::output_ports_type;
156 using second_ports_type = typename second_mf_node_type::output_ports_type;
157
158 graph g;
159
160 auto first_body = &input_type::template send_id<first_ports_type>;
161 auto second_body = &output_type1::template send_id<second_ports_type>;
162
163 first_mf_node_type mf1(g, unlimited, first_body);
164 second_mf_node_type mf21(g, unlimited, second_body);
165 second_mf_node_type mf22(g, unlimited, second_body);
166
167 buffer_node<std::size_t> buf(g);
168
169 make_edge(output_port<0>(mf1), mf21);
170 make_edge(output_port<1>(mf1), mf22);
171
172 make_edge(output_port<0>(mf21), buf);
173 make_edge(output_port<0>(mf22), buf);
174
175 mf1.try_put(input_type{output_type1{1}});
176
177 g.wait_for_all();
178
179 std::size_t buf_size = 0;
180 std::size_t tmp = 0;
181 while(buf.try_get(tmp)) {
182 ++buf_size;
183 CHECK(tmp == 1);
184 }
185
186 CHECK(buf_size == 2);
187 }
188 #endif
189