151c0b2f7Stbbdev /*
2*a088cfa0SKonstantin Boyarinov Copyright (c) 2020-2023 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
17b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19b15aabb3Stbbdev #endif
2051c0b2f7Stbbdev
21de0109beSIlya Mishin #define SEQUENCER_NODE
2251c0b2f7Stbbdev
2351c0b2f7Stbbdev #include "conformance_flowgraph.h"
24*a088cfa0SKonstantin Boyarinov #include "common/test_invoke.h"
2551c0b2f7Stbbdev
2651c0b2f7Stbbdev //! \file conformance_sequencer_node.cpp
2751c0b2f7Stbbdev //! \brief Test for [flow_graph.sequencer_node] specification
2851c0b2f7Stbbdev
2951c0b2f7Stbbdev
3051c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
31de0109beSIlya Mishin template <typename Body>
test_deduction_guides_common(Body body)32de0109beSIlya Mishin void test_deduction_guides_common(Body body) {
33de0109beSIlya Mishin using namespace tbb::flow;
34de0109beSIlya Mishin graph g;
35de0109beSIlya Mishin broadcast_node<int> br(g);
36de0109beSIlya Mishin
37de0109beSIlya Mishin sequencer_node s1(g, body);
38de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s1), sequencer_node<int>>);
39de0109beSIlya Mishin
40de0109beSIlya Mishin #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
41de0109beSIlya Mishin sequencer_node s2(follows(br), body);
42de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s2), sequencer_node<int>>);
43de0109beSIlya Mishin #endif
44de0109beSIlya Mishin
45de0109beSIlya Mishin sequencer_node s3(s1);
46de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s3), sequencer_node<int>>);
47de0109beSIlya Mishin }
48de0109beSIlya Mishin
sequencer_body_f(const int &)49de0109beSIlya Mishin std::size_t sequencer_body_f(const int&) { return 1; }
50de0109beSIlya Mishin
test_deduction_guides()5151c0b2f7Stbbdev void test_deduction_guides() {
52de0109beSIlya Mishin test_deduction_guides_common([](const int&)->std::size_t { return 1; });
53de0109beSIlya Mishin test_deduction_guides_common([](const int&) mutable ->std::size_t { return 1; });
54de0109beSIlya Mishin test_deduction_guides_common(sequencer_body_f);
5551c0b2f7Stbbdev }
5651c0b2f7Stbbdev #endif
5751c0b2f7Stbbdev
58de0109beSIlya Mishin //! Test deduction guides
59de0109beSIlya Mishin //! \brief \ref interface \ref requirement
60de0109beSIlya Mishin TEST_CASE("Deduction guides"){
61de0109beSIlya Mishin #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
62de0109beSIlya Mishin test_deduction_guides();
63de0109beSIlya Mishin #endif
6451c0b2f7Stbbdev }
6551c0b2f7Stbbdev
66de0109beSIlya Mishin //! Test sequencer_node single_push
67de0109beSIlya Mishin //! \brief \ref requirement
68de0109beSIlya Mishin TEST_CASE("sequencer_node single_push"){
69de0109beSIlya Mishin conformance::sequencer_functor<int> sequencer;
70de0109beSIlya Mishin conformance::test_forwarding_single_push<oneapi::tbb::flow::sequencer_node<int>>(sequencer);
71de0109beSIlya Mishin }
72de0109beSIlya Mishin
73de0109beSIlya Mishin //! Test function_node buffering
74de0109beSIlya Mishin //! \brief \ref requirement
75de0109beSIlya Mishin TEST_CASE("sequencer_node buffering"){
76de0109beSIlya Mishin conformance::sequencer_functor<int> sequencer;
77de0109beSIlya Mishin conformance::test_buffering<oneapi::tbb::flow::sequencer_node<int>, int>(sequencer);
78de0109beSIlya Mishin }
79de0109beSIlya Mishin
80de0109beSIlya Mishin //! Constructs an empty sequencer_node that belongs to the same graph g as src.
81de0109beSIlya Mishin //! Any intermediate state of src, including its links to predecessors and successors, is not copied.
82de0109beSIlya Mishin //! \brief \ref requirement
83de0109beSIlya Mishin TEST_CASE("sequencer_node copy constructor"){
84de0109beSIlya Mishin conformance::sequencer_functor<int> sequencer;
85de0109beSIlya Mishin conformance::test_copy_ctor_for_buffering_nodes<oneapi::tbb::flow::sequencer_node<int>>(sequencer);
86de0109beSIlya Mishin }
87de0109beSIlya Mishin
88de0109beSIlya Mishin //! Test inheritance relations
89de0109beSIlya Mishin //! \brief \ref interface
90de0109beSIlya Mishin TEST_CASE("sequencer_node superclasses"){
91de0109beSIlya Mishin conformance::test_inheritance<oneapi::tbb::flow::sequencer_node<int>, int, int>();
92de0109beSIlya Mishin conformance::test_inheritance<oneapi::tbb::flow::sequencer_node<void*>, void*, void*>();
93de0109beSIlya Mishin }
94de0109beSIlya Mishin
95de0109beSIlya Mishin //! Test the sequencer_node rejects duplicate sequencer numbers
96de0109beSIlya Mishin //! \brief \ref interface
97de0109beSIlya Mishin TEST_CASE("sequencer_node rejects duplicate"){
9849e08aacStbbdev oneapi::tbb::flow::graph g;
99de0109beSIlya Mishin conformance::sequencer_functor<int> sequencer;
100de0109beSIlya Mishin
101de0109beSIlya Mishin oneapi::tbb::flow::sequencer_node<int> node(g, sequencer);
102de0109beSIlya Mishin
103de0109beSIlya Mishin node.try_put(1);
104de0109beSIlya Mishin
105de0109beSIlya Mishin CHECK_MESSAGE((node.try_put(1) == false), "sequencer_node must rejects duplicate sequencer numbers");
106de0109beSIlya Mishin g.wait_for_all();
107de0109beSIlya Mishin }
108de0109beSIlya Mishin
109de0109beSIlya Mishin //! Test queue_node node `try_put()` and `try_get()`
110de0109beSIlya Mishin //! \brief \ref requirement
111de0109beSIlya Mishin TEST_CASE("queue_node methods"){
112de0109beSIlya Mishin oneapi::tbb::flow::graph g;
113de0109beSIlya Mishin conformance::sequencer_functor<int> sequencer;
11451c0b2f7Stbbdev
11549e08aacStbbdev oneapi::tbb::flow::sequencer_node<int> node(g, sequencer);
11651c0b2f7Stbbdev
11751c0b2f7Stbbdev node.try_put(1);
11851c0b2f7Stbbdev node.try_put(0);
11951c0b2f7Stbbdev node.try_put(1);
12051c0b2f7Stbbdev g.wait_for_all();
12151c0b2f7Stbbdev
12251c0b2f7Stbbdev int tmp = -1;
12351c0b2f7Stbbdev CHECK_MESSAGE((node.try_get(tmp) == true), "Getting from sequencer should succeed");
12451c0b2f7Stbbdev CHECK_MESSAGE((tmp == 0), "Received value should be correct");
12551c0b2f7Stbbdev
12651c0b2f7Stbbdev tmp = -1;
12751c0b2f7Stbbdev CHECK_MESSAGE((node.try_get(tmp) == true), "Getting from sequencer should succeed");
12851c0b2f7Stbbdev CHECK_MESSAGE((tmp == 1), "Received value should be correct");
12951c0b2f7Stbbdev
13051c0b2f7Stbbdev tmp = -1;
13151c0b2f7Stbbdev CHECK_MESSAGE((node.try_get(tmp) == false), "Getting from sequencer should not succeed");
13251c0b2f7Stbbdev }
13351c0b2f7Stbbdev
134de0109beSIlya Mishin //! The example demonstrates ordering capabilities of the sequencer_node.
135de0109beSIlya Mishin //! While being processed in parallel, the data is passed to the successor node in the exact same order it was read.
13651c0b2f7Stbbdev //! \brief \ref requirement
137de0109beSIlya Mishin TEST_CASE("sequencer_node ordering"){
138de0109beSIlya Mishin using namespace oneapi::tbb::flow;
139de0109beSIlya Mishin using message = conformance::sequencer_functor<int>::seq_message;
140de0109beSIlya Mishin graph g;
141de0109beSIlya Mishin
142de0109beSIlya Mishin // Due to parallelism the node can push messages to its successors in any order
__anona3025c380302(message msg) 143de0109beSIlya Mishin function_node<message, message> process(g, unlimited, [] (message msg) {
144de0109beSIlya Mishin msg.data++;
145de0109beSIlya Mishin return msg;
146de0109beSIlya Mishin });
147de0109beSIlya Mishin
148de0109beSIlya Mishin sequencer_node<message> ordering(g, conformance::sequencer_functor<int>());
149de0109beSIlya Mishin
150de0109beSIlya Mishin std::atomic<std::size_t> counter{0};
__anona3025c380402(const message& msg) 151de0109beSIlya Mishin function_node<message> writer(g, tbb::flow::serial, [&] (const message& msg) {
152de0109beSIlya Mishin CHECK_MESSAGE((msg.id == counter++), "The data is passed to the successor node in the exact same order it was read");
153de0109beSIlya Mishin });
154de0109beSIlya Mishin
155de0109beSIlya Mishin tbb::flow::make_edge(process, ordering);
156de0109beSIlya Mishin tbb::flow::make_edge(ordering, writer);
157de0109beSIlya Mishin
158de0109beSIlya Mishin for (std::size_t i = 0; i < 100; ++i) {
159de0109beSIlya Mishin message msg = {i, 0};
160de0109beSIlya Mishin process.try_put(msg);
16151c0b2f7Stbbdev }
16251c0b2f7Stbbdev
163de0109beSIlya Mishin g.wait_for_all();
16451c0b2f7Stbbdev }
165*a088cfa0SKonstantin Boyarinov
166*a088cfa0SKonstantin Boyarinov #if __TBB_CPP17_INVOKE_PRESENT
167*a088cfa0SKonstantin Boyarinov //! Test that sequencer node uses std::invoke to execute the body
168*a088cfa0SKonstantin Boyarinov //! \brief \ref requirement
169*a088cfa0SKonstantin Boyarinov TEST_CASE("sequencer_node and std::invoke") {
170*a088cfa0SKonstantin Boyarinov using namespace oneapi::tbb::flow;
171*a088cfa0SKonstantin Boyarinov
172*a088cfa0SKonstantin Boyarinov graph g;
173*a088cfa0SKonstantin Boyarinov
__anona3025c380502(std::size_t x) 174*a088cfa0SKonstantin Boyarinov function_node<std::size_t, test_invoke::SmartID<std::size_t>> starter(g, unlimited, [](std::size_t x) { return test_invoke::SmartID(x); });
175*a088cfa0SKonstantin Boyarinov sequencer_node<test_invoke::SmartID<std::size_t>> seq1(g, &test_invoke::SmartID<std::size_t>::get_id); // Member function
176*a088cfa0SKonstantin Boyarinov sequencer_node<test_invoke::SmartID<std::size_t>> seq2(g, &test_invoke::SmartID<std::size_t>::id); // Member object
177*a088cfa0SKonstantin Boyarinov
178*a088cfa0SKonstantin Boyarinov std::size_t expected_item = 0;
179*a088cfa0SKonstantin Boyarinov
__anona3025c380602(const test_invoke::SmartID<std::size_t>& x) 180*a088cfa0SKonstantin Boyarinov function_node<test_invoke::SmartID<std::size_t>, std::size_t> check(g, serial, [&](const test_invoke::SmartID<std::size_t>& x) {
181*a088cfa0SKonstantin Boyarinov CHECK(x.id == expected_item);
182*a088cfa0SKonstantin Boyarinov ++expected_item;
183*a088cfa0SKonstantin Boyarinov return x.id;
184*a088cfa0SKonstantin Boyarinov });
185*a088cfa0SKonstantin Boyarinov
186*a088cfa0SKonstantin Boyarinov // Build the first graph
187*a088cfa0SKonstantin Boyarinov make_edge(starter, seq1);
188*a088cfa0SKonstantin Boyarinov make_edge(seq1, check);
189*a088cfa0SKonstantin Boyarinov
190*a088cfa0SKonstantin Boyarinov std::size_t objects_count = 10;
191*a088cfa0SKonstantin Boyarinov for (std::size_t i = 0; i < objects_count; ++i) {
192*a088cfa0SKonstantin Boyarinov starter.try_put(objects_count - i - 1);
193*a088cfa0SKonstantin Boyarinov }
194*a088cfa0SKonstantin Boyarinov
195*a088cfa0SKonstantin Boyarinov g.wait_for_all();
196*a088cfa0SKonstantin Boyarinov
197*a088cfa0SKonstantin Boyarinov CHECK(expected_item == objects_count);
198*a088cfa0SKonstantin Boyarinov
199*a088cfa0SKonstantin Boyarinov // Rebuild the graph
200*a088cfa0SKonstantin Boyarinov g.reset(reset_flags::rf_clear_edges);
201*a088cfa0SKonstantin Boyarinov make_edge(starter, seq2);
202*a088cfa0SKonstantin Boyarinov make_edge(seq2, check);
203*a088cfa0SKonstantin Boyarinov expected_item = 0;
204*a088cfa0SKonstantin Boyarinov
205*a088cfa0SKonstantin Boyarinov for (std::size_t i = 0; i < objects_count; ++i) {
206*a088cfa0SKonstantin Boyarinov starter.try_put(objects_count - i - 1);
207*a088cfa0SKonstantin Boyarinov }
208*a088cfa0SKonstantin Boyarinov
209*a088cfa0SKonstantin Boyarinov g.wait_for_all();
210*a088cfa0SKonstantin Boyarinov
211*a088cfa0SKonstantin Boyarinov CHECK(expected_item == objects_count);
212*a088cfa0SKonstantin Boyarinov }
213*a088cfa0SKonstantin Boyarinov
214*a088cfa0SKonstantin Boyarinov #endif // __TBB_CPP17_INVOKE_PRESENT
215