1*cf0a6739SIlya Isaev /*
2*cf0a6739SIlya Isaev     Copyright (c) 2021 Intel Corporation
3*cf0a6739SIlya Isaev 
4*cf0a6739SIlya Isaev     Licensed under the Apache License, Version 2.0 (the "License");
5*cf0a6739SIlya Isaev     you may not use this file except in compliance with the License.
6*cf0a6739SIlya Isaev     You may obtain a copy of the License at
7*cf0a6739SIlya Isaev 
8*cf0a6739SIlya Isaev         http://www.apache.org/licenses/LICENSE-2.0
9*cf0a6739SIlya Isaev 
10*cf0a6739SIlya Isaev     Unless required by applicable law or agreed to in writing, software
11*cf0a6739SIlya Isaev     distributed under the License is distributed on an "AS IS" BASIS,
12*cf0a6739SIlya Isaev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*cf0a6739SIlya Isaev     See the License for the specific language governing permissions and
14*cf0a6739SIlya Isaev     limitations under the License.
15*cf0a6739SIlya Isaev */
16*cf0a6739SIlya Isaev 
17*cf0a6739SIlya Isaev #if _MSC_VER && !defined(__INTEL_COMPILER)
18*cf0a6739SIlya Isaev     // unreachable code
19*cf0a6739SIlya Isaev     #pragma warning( push )
20*cf0a6739SIlya Isaev     #pragma warning( disable: 4702 )
21*cf0a6739SIlya Isaev #endif
22*cf0a6739SIlya Isaev 
23*cf0a6739SIlya Isaev #if __INTEL_COMPILER && _MSC_VER
24*cf0a6739SIlya Isaev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
25*cf0a6739SIlya Isaev #endif
26*cf0a6739SIlya Isaev 
27*cf0a6739SIlya Isaev #include "common/test.h"
28*cf0a6739SIlya Isaev #include "oneapi/tbb/collaborative_call_once.h"
29*cf0a6739SIlya Isaev 
30*cf0a6739SIlya Isaev #include "common/utils.h"
31*cf0a6739SIlya Isaev #include "common/utils_concurrency_limit.h"
32*cf0a6739SIlya Isaev #include "common/spin_barrier.h"
33*cf0a6739SIlya Isaev #include "oneapi/tbb/parallel_for.h"
34*cf0a6739SIlya Isaev #include "oneapi/tbb/task_group.h"
35*cf0a6739SIlya Isaev 
36*cf0a6739SIlya Isaev #include <type_traits>
37*cf0a6739SIlya Isaev #include <exception>
38*cf0a6739SIlya Isaev 
39*cf0a6739SIlya Isaev //! \file conformance_collaborative_call_once.cpp
40*cf0a6739SIlya Isaev //! \brief Test for [algorithms.collaborative_call_once] specification
41*cf0a6739SIlya Isaev 
42*cf0a6739SIlya Isaev //! Test for collaborative_once_flag member functions to be matched with spec
43*cf0a6739SIlya Isaev //! \brief \ref interface \ref requirement
44*cf0a6739SIlya Isaev TEST_CASE("collaborative_once_flag member functions match") {
45*cf0a6739SIlya Isaev     REQUIRE_MESSAGE(std::is_default_constructible<oneapi::tbb::collaborative_once_flag>::value == true,
46*cf0a6739SIlya Isaev         "collaborative_once_flag must be default constructible");
47*cf0a6739SIlya Isaev     REQUIRE_MESSAGE(std::is_copy_constructible<oneapi::tbb::collaborative_once_flag>::value == false,
48*cf0a6739SIlya Isaev         "collaborative_once_flag must not be copy constructible");
49*cf0a6739SIlya Isaev     REQUIRE_MESSAGE(std::is_copy_assignable<oneapi::tbb::collaborative_once_flag>::value == false,
50*cf0a6739SIlya Isaev         "collaborative_once_flag must not be copy assignable");
51*cf0a6739SIlya Isaev     REQUIRE_MESSAGE(std::is_move_constructible<oneapi::tbb::collaborative_once_flag>::value == false,
52*cf0a6739SIlya Isaev         "collaborative_once_flag must not be move constructible");
53*cf0a6739SIlya Isaev     REQUIRE_MESSAGE(std::is_move_assignable<oneapi::tbb::collaborative_once_flag>::value == false,
54*cf0a6739SIlya Isaev         "collaborative_once_flag must not be move assignable");
55*cf0a6739SIlya Isaev }
56*cf0a6739SIlya Isaev 
57*cf0a6739SIlya Isaev //! Test for collaborative_call_once to execute function exactly once
58*cf0a6739SIlya Isaev //! \brief \ref interface \ref requirement
59*cf0a6739SIlya Isaev TEST_CASE("collaborative_call_once executes function exactly once") {
60*cf0a6739SIlya Isaev     oneapi::tbb::collaborative_once_flag once_flag;
61*cf0a6739SIlya Isaev 
62*cf0a6739SIlya Isaev     for (int iter = 0; iter < 100; ++iter) {
__anon8e61f74e0102(int number) 63*cf0a6739SIlya Isaev         oneapi::tbb::collaborative_call_once(once_flag, [](int number) {
64*cf0a6739SIlya Isaev             // Will be executed only on first iteration
65*cf0a6739SIlya Isaev             REQUIRE(number == 0);
66*cf0a6739SIlya Isaev         }, iter);
67*cf0a6739SIlya Isaev     }
68*cf0a6739SIlya Isaev 
69*cf0a6739SIlya Isaev     // concurrent call
70*cf0a6739SIlya Isaev     std::size_t num_threads = utils::get_platform_max_threads();
71*cf0a6739SIlya Isaev     utils::SpinBarrier barrier{num_threads};
72*cf0a6739SIlya Isaev 
73*cf0a6739SIlya Isaev     int flag = 0;
__anon8e61f74e0202null74*cf0a6739SIlya Isaev     auto func = [&flag] { flag++; };
75*cf0a6739SIlya Isaev 
76*cf0a6739SIlya Isaev     oneapi::tbb::collaborative_once_flag once_flag_concurrent;
__anon8e61f74e0302(std::size_t) 77*cf0a6739SIlya Isaev     utils::NativeParallelFor(num_threads, [&](std::size_t) {
78*cf0a6739SIlya Isaev         barrier.wait();
79*cf0a6739SIlya Isaev         oneapi::tbb::collaborative_call_once(once_flag_concurrent, func);
80*cf0a6739SIlya Isaev     });
81*cf0a6739SIlya Isaev     REQUIRE(flag == 1);
82*cf0a6739SIlya Isaev }
83*cf0a6739SIlya Isaev 
84*cf0a6739SIlya Isaev 
85*cf0a6739SIlya Isaev #if TBB_USE_EXCEPTIONS
86*cf0a6739SIlya Isaev 
87*cf0a6739SIlya Isaev //! Exception is received only by winner thread
88*cf0a6739SIlya Isaev //! \brief \ref error_guessing \ref requirement
89*cf0a6739SIlya Isaev TEST_CASE("Exception is received only by winner thread") {
90*cf0a6739SIlya Isaev     int num_threads = static_cast<int>(utils::get_platform_max_threads());
91*cf0a6739SIlya Isaev     utils::SpinBarrier barrier(num_threads);
92*cf0a6739SIlya Isaev 
93*cf0a6739SIlya Isaev     oneapi::tbb::task_group tg;
94*cf0a6739SIlya Isaev     oneapi::tbb::collaborative_once_flag flag;
95*cf0a6739SIlya Isaev 
96*cf0a6739SIlya Isaev     for (int i = 0; i < num_threads-1; ++i) {
__anon8e61f74e0402null97*cf0a6739SIlya Isaev         tg.run([&flag, &barrier] {
98*cf0a6739SIlya Isaev             barrier.wait();
99*cf0a6739SIlya Isaev             try {
100*cf0a6739SIlya Isaev                 oneapi::tbb::collaborative_call_once(flag, [] { });
101*cf0a6739SIlya Isaev             } catch(...) {
102*cf0a6739SIlya Isaev                 REQUIRE_MESSAGE(false, "Unreachable code");
103*cf0a6739SIlya Isaev             }
104*cf0a6739SIlya Isaev         });
105*cf0a6739SIlya Isaev     };
106*cf0a6739SIlya Isaev 
107*cf0a6739SIlya Isaev     bool exception_happened{false};
108*cf0a6739SIlya Isaev     try {
__anon8e61f74e0602null109*cf0a6739SIlya Isaev         oneapi::tbb::collaborative_call_once(flag, [&barrier] {
110*cf0a6739SIlya Isaev             barrier.wait();
111*cf0a6739SIlya Isaev             throw std::exception{};
112*cf0a6739SIlya Isaev         });
113*cf0a6739SIlya Isaev     } catch (std::exception&) {
114*cf0a6739SIlya Isaev         exception_happened = true;
115*cf0a6739SIlya Isaev     }
116*cf0a6739SIlya Isaev 
117*cf0a6739SIlya Isaev     REQUIRE_MESSAGE(exception_happened == true, "Exception hasn't been received from the winner thread");
118*cf0a6739SIlya Isaev     tg.wait();
119*cf0a6739SIlya Isaev }
120*cf0a6739SIlya Isaev 
121*cf0a6739SIlya Isaev #endif
122*cf0a6739SIlya Isaev 
123*cf0a6739SIlya Isaev #if _MSC_VER && !defined(__INTEL_COMPILER)
124*cf0a6739SIlya Isaev     #pragma warning( pop )
125*cf0a6739SIlya Isaev #endif
126