1bf1f2ab6SIlya Isaev /*
2f8102d5bSIlya Isaev Copyright (c) 2022 Intel Corporation
3bf1f2ab6SIlya Isaev
4bf1f2ab6SIlya Isaev Licensed under the Apache License, Version 2.0 (the "License");
5bf1f2ab6SIlya Isaev you may not use this file except in compliance with the License.
6bf1f2ab6SIlya Isaev You may obtain a copy of the License at
7bf1f2ab6SIlya Isaev
8bf1f2ab6SIlya Isaev http://www.apache.org/licenses/LICENSE-2.0
9bf1f2ab6SIlya Isaev
10bf1f2ab6SIlya Isaev Unless required by applicable law or agreed to in writing, software
11bf1f2ab6SIlya Isaev distributed under the License is distributed on an "AS IS" BASIS,
12bf1f2ab6SIlya Isaev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bf1f2ab6SIlya Isaev See the License for the specific language governing permissions and
14bf1f2ab6SIlya Isaev limitations under the License.
15bf1f2ab6SIlya Isaev */
16bf1f2ab6SIlya Isaev
17bf1f2ab6SIlya Isaev #if _MSC_VER && !defined(__INTEL_COMPILER)
18bf1f2ab6SIlya Isaev // unreachable code
19bf1f2ab6SIlya Isaev #pragma warning( push )
20bf1f2ab6SIlya Isaev #pragma warning( disable: 4702 )
21bf1f2ab6SIlya Isaev #endif
22bf1f2ab6SIlya Isaev
23bf1f2ab6SIlya Isaev #if __INTEL_COMPILER && _MSC_VER
24bf1f2ab6SIlya Isaev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
25bf1f2ab6SIlya Isaev #endif
26bf1f2ab6SIlya Isaev
27bf1f2ab6SIlya Isaev #include "common/config.h"
28bf1f2ab6SIlya Isaev
29bf1f2ab6SIlya Isaev // Include first to check missed header dependencies
30bf1f2ab6SIlya Isaev #include "tbb/collaborative_call_once.h"
31bf1f2ab6SIlya Isaev
32bf1f2ab6SIlya Isaev #include "common/test.h"
33bf1f2ab6SIlya Isaev #include "common/exception_handling.h"
34bf1f2ab6SIlya Isaev #include "common/utils_concurrency_limit.h"
35bf1f2ab6SIlya Isaev
36bf1f2ab6SIlya Isaev #include "tbb/parallel_invoke.h"
37bf1f2ab6SIlya Isaev #include "tbb/parallel_reduce.h"
38bf1f2ab6SIlya Isaev #include "tbb/task_arena.h"
39bf1f2ab6SIlya Isaev
40bf1f2ab6SIlya Isaev //! \file test_collaborative_call_once.cpp
41cf0a6739SIlya Isaev //! \brief Tests for [algorithms.collaborative_call_once] functionality
42bf1f2ab6SIlya Isaev
43bf1f2ab6SIlya Isaev struct increment_functor {
44bf1f2ab6SIlya Isaev int ct{0};
45bf1f2ab6SIlya Isaev
operator ()increment_functor46bf1f2ab6SIlya Isaev void operator()() {
47bf1f2ab6SIlya Isaev ct++;
48bf1f2ab6SIlya Isaev }
49bf1f2ab6SIlya Isaev };
50bf1f2ab6SIlya Isaev
51bf1f2ab6SIlya Isaev struct sum_functor {
52bf1f2ab6SIlya Isaev int sum{0};
53bf1f2ab6SIlya Isaev
54bf1f2ab6SIlya Isaev template<typename T>
operator ()sum_functor55bf1f2ab6SIlya Isaev void operator()(T first_op) {
56bf1f2ab6SIlya Isaev sum += first_op;
57bf1f2ab6SIlya Isaev }
58bf1f2ab6SIlya Isaev
59bf1f2ab6SIlya Isaev template<typename T, typename... Args>
operator ()sum_functor60bf1f2ab6SIlya Isaev void operator()(T first_op, Args&&... args) {
61bf1f2ab6SIlya Isaev (*this)(first_op);
62bf1f2ab6SIlya Isaev (*this)(std::forward<Args>(args)...);
63bf1f2ab6SIlya Isaev }
64bf1f2ab6SIlya Isaev };
65bf1f2ab6SIlya Isaev
66bf1f2ab6SIlya Isaev struct move_only_type {
67bf1f2ab6SIlya Isaev const int* my_pointer;
move_only_typemove_only_type68bf1f2ab6SIlya Isaev move_only_type(move_only_type && other): my_pointer(other.my_pointer){ other.my_pointer=nullptr; }
move_only_typemove_only_type69bf1f2ab6SIlya Isaev explicit move_only_type(const int* value): my_pointer(value) {}
70bf1f2ab6SIlya Isaev };
71bf1f2ab6SIlya Isaev
72bf1f2ab6SIlya Isaev
73bf1f2ab6SIlya Isaev class call_once_exception : public std::exception {};
74bf1f2ab6SIlya Isaev
75bf1f2ab6SIlya Isaev template<typename Fn, typename... Args>
call_once_in_for_loop(std::size_t N,Fn && body,Args &&...args)76bf1f2ab6SIlya Isaev void call_once_in_for_loop(std::size_t N, Fn&& body, Args&&... args) {
77bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
78bf1f2ab6SIlya Isaev for (std::size_t i = 0; i < N; ++i) {
79bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, std::forward<Fn>(body), std::forward<Args>(args)...);
80bf1f2ab6SIlya Isaev }
81bf1f2ab6SIlya Isaev }
82bf1f2ab6SIlya Isaev
83bf1f2ab6SIlya Isaev template<typename Fn, typename... Args>
call_once_in_parallel_for(std::size_t N,Fn && body,Args &&...args)84bf1f2ab6SIlya Isaev void call_once_in_parallel_for(std::size_t N, Fn&& body, Args&&... args) {
85bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
86bf1f2ab6SIlya Isaev #if __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
87bf1f2ab6SIlya Isaev auto stored_pack = tbb::detail::d0::save_pack(std::forward<Args>(args)...);
88bf1f2ab6SIlya Isaev auto func = [&] { tbb::detail::d0::call(std::forward<Fn>(body), std::move(stored_pack)); };
89bf1f2ab6SIlya Isaev #endif // __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
90bf1f2ab6SIlya Isaev
91bf1f2ab6SIlya Isaev tbb::parallel_for(tbb::blocked_range<size_t>(0, N), [&](const tbb::blocked_range<size_t>& range) {
92bf1f2ab6SIlya Isaev for (size_t i = range.begin(); i != range.end(); ++i) {
93bf1f2ab6SIlya Isaev #if __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
94bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, func);
95bf1f2ab6SIlya Isaev #else
96bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, std::forward<Fn>(body), std::forward<Args>(args)...);
97bf1f2ab6SIlya Isaev #endif //__TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
98bf1f2ab6SIlya Isaev }
99bf1f2ab6SIlya Isaev });
100bf1f2ab6SIlya Isaev }
101bf1f2ab6SIlya Isaev
102bf1f2ab6SIlya Isaev template<typename Fn, typename... Args>
call_once_threads(std::size_t N,Fn && body,Args &&...args)103bf1f2ab6SIlya Isaev void call_once_threads(std::size_t N, Fn&& body, Args&&... args) {
104bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
105bf1f2ab6SIlya Isaev std::vector<std::thread> threads;
106bf1f2ab6SIlya Isaev
107bf1f2ab6SIlya Isaev #if __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
108bf1f2ab6SIlya Isaev auto stored_pack = tbb::detail::d0::save_pack(std::forward<Args>(args)...);
109bf1f2ab6SIlya Isaev auto func = [&] { tbb::detail::d0::call(std::forward<Fn>(body), std::move(stored_pack)); };
110bf1f2ab6SIlya Isaev #endif // __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
111bf1f2ab6SIlya Isaev
112bf1f2ab6SIlya Isaev for (std::size_t i = 0; i < N; ++i)
113bf1f2ab6SIlya Isaev {
114bf1f2ab6SIlya Isaev threads.push_back(std::thread([&]() {
115bf1f2ab6SIlya Isaev #if __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
116bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, func);
117bf1f2ab6SIlya Isaev #else
118bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, std::forward<Fn>(body), std::forward<Args>(args)...);
119bf1f2ab6SIlya Isaev #endif // __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN
120bf1f2ab6SIlya Isaev }));
121bf1f2ab6SIlya Isaev }
122bf1f2ab6SIlya Isaev for (auto& thread : threads) {
123bf1f2ab6SIlya Isaev thread.join();
124bf1f2ab6SIlya Isaev }
125bf1f2ab6SIlya Isaev }
126bf1f2ab6SIlya Isaev
127bf1f2ab6SIlya Isaev //! Test for functor to be called only once
128bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement
129bf1f2ab6SIlya Isaev TEST_CASE("only calls once 1") {
130bf1f2ab6SIlya Isaev {
131bf1f2ab6SIlya Isaev increment_functor f;
132bf1f2ab6SIlya Isaev
133bf1f2ab6SIlya Isaev call_once_in_for_loop(1024, f);
134bf1f2ab6SIlya Isaev
135bf1f2ab6SIlya Isaev REQUIRE( f.ct == 1);
136bf1f2ab6SIlya Isaev }
137bf1f2ab6SIlya Isaev {
138bf1f2ab6SIlya Isaev increment_functor f;
139bf1f2ab6SIlya Isaev
140bf1f2ab6SIlya Isaev call_once_in_parallel_for(100, f);
141bf1f2ab6SIlya Isaev
142bf1f2ab6SIlya Isaev REQUIRE(f.ct == 1);
143bf1f2ab6SIlya Isaev }
144bf1f2ab6SIlya Isaev {
145bf1f2ab6SIlya Isaev increment_functor f;
146bf1f2ab6SIlya Isaev
147bf1f2ab6SIlya Isaev call_once_threads(utils::get_platform_max_threads(), f);
148bf1f2ab6SIlya Isaev
149bf1f2ab6SIlya Isaev REQUIRE(f.ct == 1);
150bf1f2ab6SIlya Isaev }
151bf1f2ab6SIlya Isaev }
152bf1f2ab6SIlya Isaev
153bf1f2ab6SIlya Isaev //! Test for functor to be called only once
154bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement
155bf1f2ab6SIlya Isaev TEST_CASE("only calls once 2") {
156bf1f2ab6SIlya Isaev {
157bf1f2ab6SIlya Isaev sum_functor f;
158bf1f2ab6SIlya Isaev
159bf1f2ab6SIlya Isaev call_once_in_for_loop(1024, f, 1, 2, 3 ,4);
160bf1f2ab6SIlya Isaev
161bf1f2ab6SIlya Isaev REQUIRE(f.sum == 10);
162bf1f2ab6SIlya Isaev }
163bf1f2ab6SIlya Isaev {
164bf1f2ab6SIlya Isaev sum_functor f;
165bf1f2ab6SIlya Isaev
166bf1f2ab6SIlya Isaev call_once_in_parallel_for(512, f, 1000, -1000);
167bf1f2ab6SIlya Isaev
168bf1f2ab6SIlya Isaev REQUIRE(f.sum == 0);
169bf1f2ab6SIlya Isaev }
170bf1f2ab6SIlya Isaev {
171bf1f2ab6SIlya Isaev sum_functor f;
172bf1f2ab6SIlya Isaev
173bf1f2ab6SIlya Isaev call_once_threads(utils::get_platform_max_threads(), f, 0, -1, -5);
174bf1f2ab6SIlya Isaev
175bf1f2ab6SIlya Isaev REQUIRE(f.sum == -6);
176bf1f2ab6SIlya Isaev }
177bf1f2ab6SIlya Isaev }
178bf1f2ab6SIlya Isaev
179bf1f2ab6SIlya Isaev //! Test for correct handling move-only arguments
180bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement
181bf1f2ab6SIlya Isaev TEST_CASE("only calls once - move only argument") {
182bf1f2ab6SIlya Isaev const int value = 42;
183bf1f2ab6SIlya Isaev int ready{0};
184bf1f2ab6SIlya Isaev
__anon8532fc100502(move_only_type other) 185bf1f2ab6SIlya Isaev auto func = [&ready, &value] (move_only_type other) {
186bf1f2ab6SIlya Isaev REQUIRE(other.my_pointer == &value);
187bf1f2ab6SIlya Isaev ready++;
188bf1f2ab6SIlya Isaev };
189bf1f2ab6SIlya Isaev
190bf1f2ab6SIlya Isaev {
191bf1f2ab6SIlya Isaev move_only_type mv(&value);
192bf1f2ab6SIlya Isaev
193bf1f2ab6SIlya Isaev call_once_in_parallel_for(512, func, std::move(mv));
194bf1f2ab6SIlya Isaev
195bf1f2ab6SIlya Isaev REQUIRE(ready == 1);
196bf1f2ab6SIlya Isaev REQUIRE(mv.my_pointer == nullptr);
197bf1f2ab6SIlya Isaev }
198bf1f2ab6SIlya Isaev
199bf1f2ab6SIlya Isaev {
200bf1f2ab6SIlya Isaev move_only_type mv(&value);
201bf1f2ab6SIlya Isaev
202bf1f2ab6SIlya Isaev call_once_threads(utils::get_platform_max_threads(), func, std::move(mv));
203bf1f2ab6SIlya Isaev
204bf1f2ab6SIlya Isaev REQUIRE(ready == 2);
205bf1f2ab6SIlya Isaev REQUIRE(mv.my_pointer == nullptr);
206bf1f2ab6SIlya Isaev }
207bf1f2ab6SIlya Isaev }
208bf1f2ab6SIlya Isaev
209bf1f2ab6SIlya Isaev //! Stress test for functor to be called only once
210bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement \ref stress
211bf1f2ab6SIlya Isaev TEST_CASE("only calls once - stress test") {
212bf1f2ab6SIlya Isaev #if TBB_TEST_LOW_WORKLOAD
213bf1f2ab6SIlya Isaev constexpr std::size_t N = 32;
214f8102d5bSIlya Isaev #elif __TBB_x86_32 || __arm__ || __ANDROID__
215bf1f2ab6SIlya Isaev // Some C++ implementations allocate 8MB stacks for std::thread on 32 bit platforms
216bf1f2ab6SIlya Isaev // that makes impossible to create more than ~500 threads.
217bf1f2ab6SIlya Isaev // Android has been added to decrease testing time.
218bf1f2ab6SIlya Isaev constexpr std::size_t N = tbb::detail::d0::max_nfs_size * 2;
219*0f0d1cb9SPavel Kumbrasev #elif __TBB_USE_THREAD_SANITIZER
220*0f0d1cb9SPavel Kumbrasev // Reduce execution time under Thread Sanitizer
221*0f0d1cb9SPavel Kumbrasev constexpr std::size_t N = tbb::detail::d0::max_nfs_size + 64;
222bf1f2ab6SIlya Isaev #else
223bf1f2ab6SIlya Isaev constexpr std::size_t N = tbb::detail::d0::max_nfs_size * 4;
224bf1f2ab6SIlya Isaev #endif
225bf1f2ab6SIlya Isaev {
226bf1f2ab6SIlya Isaev increment_functor f;
227bf1f2ab6SIlya Isaev
228bf1f2ab6SIlya Isaev call_once_threads(N, f);
229bf1f2ab6SIlya Isaev
230bf1f2ab6SIlya Isaev REQUIRE(f.ct == 1);
231bf1f2ab6SIlya Isaev }
232bf1f2ab6SIlya Isaev {
233bf1f2ab6SIlya Isaev increment_functor f;
234bf1f2ab6SIlya Isaev
235bf1f2ab6SIlya Isaev utils::SpinBarrier barrier{N};
236bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
__anon8532fc100602(std::size_t) 237bf1f2ab6SIlya Isaev utils::NativeParallelFor(N, [&](std::size_t) {
238bf1f2ab6SIlya Isaev for (int i = 0; i < 100; ++i) {
239bf1f2ab6SIlya Isaev REQUIRE(f.ct == i);
240bf1f2ab6SIlya Isaev barrier.wait([&] {
241bf1f2ab6SIlya Isaev flag.~collaborative_once_flag();
242bf1f2ab6SIlya Isaev new (&flag) tbb::collaborative_once_flag{};
243bf1f2ab6SIlya Isaev });
244bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, f);
245bf1f2ab6SIlya Isaev }
246bf1f2ab6SIlya Isaev });
247bf1f2ab6SIlya Isaev }
248bf1f2ab6SIlya Isaev }
249bf1f2ab6SIlya Isaev
250bf1f2ab6SIlya Isaev #if TBB_USE_EXCEPTIONS
251bf1f2ab6SIlya Isaev
252bf1f2ab6SIlya Isaev //! Test for collaborative_call_once exception handling
253bf1f2ab6SIlya Isaev //! \brief \ref error_guessing
254bf1f2ab6SIlya Isaev TEST_CASE("handles exceptions - state reset") {
255bf1f2ab6SIlya Isaev bool b{ false };
__anon8532fc100802() 256bf1f2ab6SIlya Isaev auto setB = [&b]() { b = true; };
__anon8532fc100902() 257bf1f2ab6SIlya Isaev auto setBAndCancel = [&b]() {
258bf1f2ab6SIlya Isaev b = true;
259bf1f2ab6SIlya Isaev throw call_once_exception{};
260bf1f2ab6SIlya Isaev };
261bf1f2ab6SIlya Isaev
262bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
263bf1f2ab6SIlya Isaev REQUIRE_THROWS_AS(tbb::collaborative_call_once(flag, setBAndCancel), call_once_exception);
264bf1f2ab6SIlya Isaev REQUIRE(b);
265bf1f2ab6SIlya Isaev
266bf1f2ab6SIlya Isaev b = false;
267bf1f2ab6SIlya Isaev REQUIRE_THROWS_AS(tbb::collaborative_call_once(flag, setBAndCancel), call_once_exception);
268bf1f2ab6SIlya Isaev REQUIRE(b);
269bf1f2ab6SIlya Isaev
270bf1f2ab6SIlya Isaev b = false;
271bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, setB);
272bf1f2ab6SIlya Isaev REQUIRE(b);
273bf1f2ab6SIlya Isaev
274bf1f2ab6SIlya Isaev b = false;
275bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, setB); // Now the call_once flag should be set.
276bf1f2ab6SIlya Isaev REQUIRE(!b);
277bf1f2ab6SIlya Isaev
278bf1f2ab6SIlya Isaev b = false;
279bf1f2ab6SIlya Isaev REQUIRE_NOTHROW(tbb::collaborative_call_once(flag, setBAndCancel)); // Flag still set, so it shouldn't be called.
280bf1f2ab6SIlya Isaev REQUIRE(!b);
281bf1f2ab6SIlya Isaev }
282bf1f2ab6SIlya Isaev
283bf1f2ab6SIlya Isaev //! Stress test for collaborative_call_once exception handling
284bf1f2ab6SIlya Isaev //! \brief \ref error_guessing \ref stress
285bf1f2ab6SIlya Isaev TEST_CASE("handles exceptions - stress test") {
286bf1f2ab6SIlya Isaev #if TBB_TEST_LOW_WORKLOAD
287bf1f2ab6SIlya Isaev constexpr std::size_t N = 32;
288f8102d5bSIlya Isaev #elif __TBB_x86_32 || __arm__ || __ANDROID__
289bf1f2ab6SIlya Isaev // Some C++ implementations allocate 8MB stacks for std::thread on 32 bit platforms
290bf1f2ab6SIlya Isaev // that makes impossible to create more than ~500 threads.
291bf1f2ab6SIlya Isaev // Android has been added to decrease testing time.
292bf1f2ab6SIlya Isaev constexpr std::size_t N = tbb::detail::d0::max_nfs_size * 2;
293bf1f2ab6SIlya Isaev #else
294bf1f2ab6SIlya Isaev constexpr std::size_t N = tbb::detail::d0::max_nfs_size * 4;
295bf1f2ab6SIlya Isaev #endif
296bf1f2ab6SIlya Isaev
297bf1f2ab6SIlya Isaev int data{0};
2984beec37aSIlya Isaev std::atomic<bool> run_again{true};
299bf1f2ab6SIlya Isaev
__anon8532fc100a02null300bf1f2ab6SIlya Isaev auto throwing_func = [&] {
301bf1f2ab6SIlya Isaev utils::doDummyWork(10000);
302bf1f2ab6SIlya Isaev if (data < 100) {
303bf1f2ab6SIlya Isaev data++;
304bf1f2ab6SIlya Isaev throw call_once_exception{};
305bf1f2ab6SIlya Isaev }
306bf1f2ab6SIlya Isaev run_again = false;
307bf1f2ab6SIlya Isaev };
308bf1f2ab6SIlya Isaev
309bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
310bf1f2ab6SIlya Isaev
__anon8532fc100b02(std::size_t) 311bf1f2ab6SIlya Isaev utils::NativeParallelFor(N, [&](std::size_t) {
312bf1f2ab6SIlya Isaev while(run_again) {
313bf1f2ab6SIlya Isaev try {
314bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, throwing_func);
315bf1f2ab6SIlya Isaev } catch (const call_once_exception&) {
316bf1f2ab6SIlya Isaev // We expecting only const call_once_exception&
317bf1f2ab6SIlya Isaev } catch (...) {
318bf1f2ab6SIlya Isaev FAIL("Unexpected exception");
319bf1f2ab6SIlya Isaev }
320bf1f2ab6SIlya Isaev }
321bf1f2ab6SIlya Isaev });
322bf1f2ab6SIlya Isaev REQUIRE(data == 100);
323bf1f2ab6SIlya Isaev }
324bf1f2ab6SIlya Isaev
325bf1f2ab6SIlya Isaev #endif
326bf1f2ab6SIlya Isaev
327bf1f2ab6SIlya Isaev //! Test for multiple help from moonlighting threads
328bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement
329bf1f2ab6SIlya Isaev TEST_CASE("multiple help") {
330bf1f2ab6SIlya Isaev std::size_t num_threads = utils::get_platform_max_threads();
331bf1f2ab6SIlya Isaev utils::SpinBarrier barrier{num_threads};
332bf1f2ab6SIlya Isaev
333bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
334bf1f2ab6SIlya Isaev
__anon8532fc100c02(std::size_t) 335bf1f2ab6SIlya Isaev tbb::parallel_for<std::size_t>(0, num_threads, [&](std::size_t) {
336bf1f2ab6SIlya Isaev barrier.wait();
337bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, [&] {
338bf1f2ab6SIlya Isaev tbb::parallel_for<std::size_t>(0, num_threads, [&](std::size_t) {
339bf1f2ab6SIlya Isaev barrier.wait();
340bf1f2ab6SIlya Isaev });
341bf1f2ab6SIlya Isaev });
342bf1f2ab6SIlya Isaev });
343bf1f2ab6SIlya Isaev }
344bf1f2ab6SIlya Isaev
345bf1f2ab6SIlya Isaev //! Test for collaborative work from different arenas
346bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement
347bf1f2ab6SIlya Isaev TEST_CASE("multiple arenas") {
34855f9b178SIvan Kochin int num_threads = static_cast<int>(utils::get_platform_max_threads());
349bf1f2ab6SIlya Isaev utils::SpinBarrier barrier(num_threads);
350bf1f2ab6SIlya Isaev tbb::task_arena a1(num_threads), a2(num_threads);
351bf1f2ab6SIlya Isaev
352bf1f2ab6SIlya Isaev tbb::collaborative_once_flag flag;
35355f9b178SIvan Kochin for (auto i = 0; i < num_threads - 1; ++i) {
__anon8532fc100f02null354bf1f2ab6SIlya Isaev a1.enqueue([&] {
355bf1f2ab6SIlya Isaev barrier.wait();
356bf1f2ab6SIlya Isaev barrier.wait();
357bf1f2ab6SIlya Isaev
358bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, [] {
359bf1f2ab6SIlya Isaev FAIL("Unreachable code. collaborative_once_flag must be already initialized at this moment");
360bf1f2ab6SIlya Isaev });
361a080baf9SAlex // To sync collaborative_once_flag lifetime
362a080baf9SAlex barrier.wait();
363bf1f2ab6SIlya Isaev });
364bf1f2ab6SIlya Isaev }
365bf1f2ab6SIlya Isaev
366bf1f2ab6SIlya Isaev barrier.wait();
367bf1f2ab6SIlya Isaev
__anon8532fc101102null368bf1f2ab6SIlya Isaev a2.execute([&] {
369bf1f2ab6SIlya Isaev utils::ConcurrencyTracker ct;
37055f9b178SIvan Kochin tbb::parallel_for(0, num_threads, [&](int) {
371bf1f2ab6SIlya Isaev CHECK(utils::ConcurrencyTracker::PeakParallelism() == 1);
372bf1f2ab6SIlya Isaev });
373bf1f2ab6SIlya Isaev tbb::collaborative_call_once(flag, [&] {
374bf1f2ab6SIlya Isaev barrier.wait();
37555f9b178SIvan Kochin tbb::parallel_for(0, num_threads, [&](int) {
376bf1f2ab6SIlya Isaev barrier.wait();
377bf1f2ab6SIlya Isaev });
378bf1f2ab6SIlya Isaev });
379a080baf9SAlex // To sync collaborative_once_flag lifetime
380a080baf9SAlex barrier.wait();
381bf1f2ab6SIlya Isaev });
382bf1f2ab6SIlya Isaev }
383bf1f2ab6SIlya Isaev
384bf1f2ab6SIlya Isaev using FibBuffer = std::vector<std::pair<tbb::collaborative_once_flag, std::uint64_t>>;
collaborative_recursive_fib(int n,FibBuffer & buffer)385bf1f2ab6SIlya Isaev std::uint64_t collaborative_recursive_fib(int n, FibBuffer& buffer) {
386bf1f2ab6SIlya Isaev if (n <= 1) {
387bf1f2ab6SIlya Isaev return 1;
388bf1f2ab6SIlya Isaev }
389bf1f2ab6SIlya Isaev tbb::collaborative_call_once(buffer[n].first, [&]() {
390bf1f2ab6SIlya Isaev std::uint64_t a, b;
391bf1f2ab6SIlya Isaev tbb::parallel_invoke([&] { a = collaborative_recursive_fib(n - 2, buffer); },
392bf1f2ab6SIlya Isaev [&] { b = collaborative_recursive_fib(n - 1, buffer); });
393bf1f2ab6SIlya Isaev buffer[n].second = a + b;
394bf1f2ab6SIlya Isaev });
395bf1f2ab6SIlya Isaev return buffer[n].second;
396bf1f2ab6SIlya Isaev }
397bf1f2ab6SIlya Isaev
collaborative_recursive_fib(int n)398bf1f2ab6SIlya Isaev std::uint64_t collaborative_recursive_fib(int n) {
399bf1f2ab6SIlya Isaev FibBuffer buffer(n);
400bf1f2ab6SIlya Isaev return collaborative_recursive_fib(n-1, buffer);
401bf1f2ab6SIlya Isaev }
402bf1f2ab6SIlya Isaev
403bf1f2ab6SIlya Isaev //! Correctness test for Fibonacci example
404bf1f2ab6SIlya Isaev //! \brief \ref interface \ref requirement
405bf1f2ab6SIlya Isaev TEST_CASE("fibonacci example") {
406bf1f2ab6SIlya Isaev constexpr int N = 93;
407bf1f2ab6SIlya Isaev constexpr std::uint64_t expected_result = 12200160415121876738ull;
408bf1f2ab6SIlya Isaev
409bf1f2ab6SIlya Isaev auto collaborative = collaborative_recursive_fib(N);
410bf1f2ab6SIlya Isaev
411bf1f2ab6SIlya Isaev REQUIRE(collaborative == expected_result);
412bf1f2ab6SIlya Isaev }
413bf1f2ab6SIlya Isaev
414bf1f2ab6SIlya Isaev #if _MSC_VER && !defined(__INTEL_COMPILER)
415bf1f2ab6SIlya Isaev #pragma warning( pop )
416bf1f2ab6SIlya Isaev #endif
417