1 /*
2 Copyright (c) 2005-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
18 #include "common/config.h"
19
20 #include "tbb/flow_graph.h"
21
22 #include "common/test.h"
23 #include "common/utils.h"
24 #include "common/utils_assert.h"
25 #include "common/graph_utils.h"
26 #include "common/test_follows_and_precedes_api.h"
27
28 #define N 300
29 #define T 4
30 #define M 4
31
32
33 //! \file test_write_once_node.cpp
34 //! \brief Test for [flow_graph.write_once_node] specification
35
36
37 template< typename R >
simple_read_write_tests()38 void simple_read_write_tests() {
39 tbb::flow::graph g;
40 tbb::flow::write_once_node<R> n(g);
41
42 for ( int t = 0; t < T; ++t ) {
43 R v0(0);
44 std::vector< std::shared_ptr<harness_counting_receiver<R>> > r;
45 for (size_t i = 0; i < M; ++i) {
46 r.push_back( std::make_shared<harness_counting_receiver<R>>(g) );
47 }
48
49
50 CHECK_MESSAGE( n.is_valid() == false, "" );
51 CHECK_MESSAGE( n.try_get( v0 ) == false, "" );
52
53 if ( t % 2 ) {
54 CHECK_MESSAGE( n.try_put( static_cast<R>(N+1) ), "" );
55 CHECK_MESSAGE( n.is_valid() == true, "" );
56 CHECK_MESSAGE( n.try_get( v0 ) == true, "" );
57 CHECK_MESSAGE( v0 == R(N+1), "" );
58 }
59
60 for (int i = 0; i < M; ++i) {
61 tbb::flow::make_edge( n, *r[i] );
62 }
63
64 if ( t%2 ) {
65 for (int i = 0; i < M; ++i) {
66 size_t c = r[i]->my_count;
67 CHECK_MESSAGE( int(c) == 1, "" );
68 }
69 }
70
71 for (int i = 1; i <= N; ++i ) {
72 R v1(static_cast<R>(i));
73
74 bool result = n.try_put( v1 );
75 if ( !(t%2) && i == 1 )
76 CHECK_MESSAGE( result == true, "" );
77 else
78 CHECK_MESSAGE( result == false, "" );
79
80 CHECK_MESSAGE( n.is_valid() == true, "" );
81
82 for (int j = 0; j < N; ++j ) {
83 R v2(0);
84 CHECK_MESSAGE( n.try_get( v2 ), "" );
85 if ( t%2 )
86 CHECK_MESSAGE( R(N+1) == v2, "" );
87 else
88 CHECK_MESSAGE( R(1) == v2, "" );
89 }
90 }
91 for (int i = 0; i < M; ++i) {
92 size_t c = r[i]->my_count;
93 CHECK_MESSAGE( int(c) == 1, "" );
94 }
95 for (int i = 0; i < M; ++i) {
96 tbb::flow::remove_edge( n, *r[i] );
97 }
98 CHECK_MESSAGE( n.try_put( R(0) ) == false, "" );
99 for (int i = 0; i < M; ++i) {
100 size_t c = r[i]->my_count;
101 CHECK_MESSAGE( int(c) == 1, "" );
102 }
103 n.clear();
104 CHECK_MESSAGE( n.is_valid() == false, "" );
105 CHECK_MESSAGE( n.try_get( v0 ) == false, "" );
106 }
107 }
108
109 template< typename R >
110 class native_body : utils::NoAssign {
111 tbb::flow::write_once_node<R> &my_node;
112
113 public:
114
native_body(tbb::flow::write_once_node<R> & n)115 native_body( tbb::flow::write_once_node<R> &n ) : my_node(n) {}
116
operator ()(int i) const117 void operator()( int i ) const {
118 R v1(static_cast<R>(i));
119 CHECK_MESSAGE( my_node.try_put( v1 ) == false, "" );
120 CHECK_MESSAGE( my_node.is_valid() == true, "" );
121 CHECK_MESSAGE( my_node.try_get( v1 ) == true, "" );
122 CHECK_MESSAGE( v1 == R(-1), "" );
123 }
124 };
125
126 template< typename R >
parallel_read_write_tests()127 void parallel_read_write_tests() {
128 tbb::flow::graph g;
129 tbb::flow::write_once_node<R> n(g);
130 //Create a vector of identical nodes
131 std::vector< tbb::flow::write_once_node<R> > wo_vec(2, n);
132
133 for (size_t node_idx=0; node_idx<wo_vec.size(); ++node_idx) {
134 for ( int t = 0; t < T; ++t ) {
135 std::vector< std::shared_ptr<harness_counting_receiver<R>> > r;
136 for (size_t i = 0; i < M; ++i) {
137 r.push_back( std::make_shared<harness_counting_receiver<R>>(g) );
138 }
139
140
141 for (int i = 0; i < M; ++i) {
142 tbb::flow::make_edge( wo_vec[node_idx], *r[i] );
143 }
144 R v0;
145 CHECK_MESSAGE( wo_vec[node_idx].is_valid() == false, "" );
146 CHECK_MESSAGE( wo_vec[node_idx].try_get( v0 ) == false, "" );
147
148 CHECK_MESSAGE( wo_vec[node_idx].try_put( R(-1) ), "" );
149 #if TBB_TEST_LOW_WORKLOAD
150 const int nthreads = 30;
151 #else
152 const int nthreads = N;
153 #endif
154 utils::NativeParallelFor( nthreads, native_body<R>( wo_vec[node_idx] ) );
155
156 for (int i = 0; i < M; ++i) {
157 size_t c = r[i]->my_count;
158 CHECK_MESSAGE( int(c) == 1, "" );
159 }
160 for (int i = 0; i < M; ++i) {
161 tbb::flow::remove_edge( wo_vec[node_idx], *r[i] );
162 }
163 CHECK_MESSAGE( wo_vec[node_idx].try_put( R(0) ) == false, "" );
164 for (int i = 0; i < M; ++i) {
165 size_t c = r[i]->my_count;
166 CHECK_MESSAGE( int(c) == 1, "" );
167 }
168 wo_vec[node_idx].clear();
169 CHECK_MESSAGE( wo_vec[node_idx].is_valid() == false, "" );
170 CHECK_MESSAGE( wo_vec[node_idx].try_get( v0 ) == false, "" );
171 }
172 }
173 }
174
175 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
176 #include <array>
177 #include <vector>
test_follows_and_precedes_api()178 void test_follows_and_precedes_api() {
179 using msg_t = tbb::flow::continue_msg;
180
181 std::array<msg_t, 3> messages_for_follows= {msg_t(), msg_t(), msg_t()};
182 std::vector<msg_t> messages_for_precedes = {msg_t()};
183
184 follows_and_precedes_testing::test_follows<msg_t, tbb::flow::write_once_node<msg_t>>(messages_for_follows);
185 follows_and_precedes_testing::test_precedes<msg_t, tbb::flow::write_once_node<msg_t>>(messages_for_precedes);
186 }
187 #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
188
189 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
test_deduction_guides()190 void test_deduction_guides() {
191 using namespace tbb::flow;
192
193 graph g;
194 broadcast_node<int> b1(g);
195 write_once_node<int> wo0(g);
196
197 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
198 write_once_node wo1(follows(b1));
199 static_assert(std::is_same_v<decltype(wo1), write_once_node<int>>);
200
201 write_once_node wo2(precedes(b1));
202 static_assert(std::is_same_v<decltype(wo2), write_once_node<int>>);
203 #endif
204
205 write_once_node wo3(wo0);
206 static_assert(std::is_same_v<decltype(wo3), write_once_node<int>>);
207 }
208 #endif
209
210 //! Test read-write properties
211 //! \brief \ref requirement \ref error_guessing
212 TEST_CASE("Read-write tests"){
213 simple_read_write_tests<int>();
214 simple_read_write_tests<float>();
215 }
216
217 //! Test read-write properties under parallelism
218 //! \brief \ref requirement \ref error_guessing \ref stress
219 TEST_CASE("Parallel read-write tests"){
220 for( unsigned int p=utils::MinThread; p<=utils::MaxThread; ++p ) {
221 tbb::task_arena arena(p);
222 arena.execute(
__anon67c9833e0102() 223 [&]() {
224 parallel_read_write_tests<int>();
225 parallel_read_write_tests<float>();
226 test_reserving_nodes<tbb::flow::write_once_node, size_t>();
227 }
228 );
229 }
230 }
231
232 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
233 //! Test deprecated follows and precedes API
234 //! \brief \ref error_guessing
235 TEST_CASE("Test follows and precedes API"){
236 test_follows_and_precedes_api();
237 }
238 #endif
239
240 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
241 //! Test deduction guides
242 //! \brief \ref requirement
243 TEST_CASE("Deduction guides"){
244 test_deduction_guides();
245 }
246 #endif
247