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