1 /*
2     Copyright (c) 2020 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 #include "common/test.h"
18 #include "common/utils.h"
19 #include "common/cpu_usertime.h"
20 #include "common/utils_concurrency_limit.h"
21 #include "common/parallel_invoke_common.h"
22 #include "common/memory_usage.h"
23 
24 #include <cstddef>
25 #include <cstdint>
26 #include <atomic>
27 
28 //! \file conformance_parallel_invoke.cpp
29 //! \brief Test for [algorithms.parallel_invoke] specification
30 
31 template<std::size_t TaskCount>
32 struct correctness_test_case {
33     static std::atomic<std::size_t> data_array[TaskCount];
34 
35     // invocation functor
36     template<std::size_t Position>
37     struct functor {
38 
39         functor() = default;
40         functor(const functor&) = delete;
41         functor& operator=(const functor&) = delete;
42 
43         void operator()() const {
44             REQUIRE_MESSAGE(Position < TaskCount, "Wrong structure configuration.");
45             data_array[Position]++;
46         }
47     };
48 
49     static void run_validate_and_reset(oneapi::tbb::task_group_context* context_ptr) {
50         for (auto& elem : data_array)
51             elem.store(0, std::memory_order_relaxed);
52 
53         parallel_invoke_call<TaskCount, functor>::perform(context_ptr);
54         for (std::size_t i = 0; i < TaskCount; i++) {
55             REQUIRE_MESSAGE(data_array[i] == 1, "Some task was executed more than once, or was not executed.");
56             data_array[i] = 0;
57         }
58     }
59 };
60 
61 template<std::size_t TaskCount>
62 std::atomic<std::size_t> correctness_test_case<TaskCount>::data_array[TaskCount];
63 
64 void correctness_test(oneapi::tbb::task_group_context* context_ptr = nullptr) {
65     for ( auto concurrency_level : utils::concurrency_range() ) {
66         oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
67 
68         correctness_test_case<2>::run_validate_and_reset(context_ptr);
69         correctness_test_case<3>::run_validate_and_reset(context_ptr);
70         correctness_test_case<4>::run_validate_and_reset(context_ptr);
71         correctness_test_case<5>::run_validate_and_reset(context_ptr);
72         correctness_test_case<6>::run_validate_and_reset(context_ptr);
73         correctness_test_case<7>::run_validate_and_reset(context_ptr);
74         correctness_test_case<8>::run_validate_and_reset(context_ptr);
75         correctness_test_case<9>::run_validate_and_reset(context_ptr);
76         correctness_test_case<10>::run_validate_and_reset(context_ptr);
77         // ...
78         correctness_test_case<50>::run_validate_and_reset(context_ptr);
79         correctness_test_case<51>::run_validate_and_reset(context_ptr);
80         correctness_test_case<52>::run_validate_and_reset(context_ptr);
81     }
82 }
83 
84 //! Testing correctness with various functors count
85 //! \brief \ref requirement \ref interface
86 TEST_CASE("Test correctness") {
87     correctness_test();
88 }
89 
90 //! Testing correctness with various functors count using task_group_context
91 //! \brief \ref requirement \ref interface
92 TEST_CASE("Test correctness using context") {
93     oneapi::tbb::task_group_context context;
94     correctness_test(&context);
95 }
96 
97 // Exception handling support test
98 #define UTILS_EXCEPTION_HANDLING_SIMPLE_MODE 1
99 #include "common/exception_handling.h"
100 
101 #if TBB_USE_EXCEPTIONS
102 
103 template<std::size_t TaskCount>
104 struct exception_handling_test_case {
105     // invocation functor
106     template<std::size_t Position>
107     struct functor {
108         functor() = default;
109         functor(const functor&) = delete;
110         functor& operator=(const functor&) = delete;
111 
112         void operator()() const {
113             REQUIRE_MESSAGE(Position < TaskCount, "Wrong structure configuration.");
114             if (exception_mask & (1 << Position)) {
115                 ThrowTestException();
116             }
117         }
118     };
119 
120     static void run_validate_and_reset() {
121         // Checks all permutations of the exception handling mask for the current tasks count
122         for( exception_mask = 1; exception_mask < (std::size_t(1) << TaskCount); ++exception_mask ) {
123             ResetEhGlobals();
124             TRY();
125                 parallel_invoke_call<TaskCount, functor>::perform();
126             CATCH();
127             ASSERT_EXCEPTION();
128         }
129     }
130 private:
131     static std::uint64_t exception_mask; // each bit represents whether the function should throw exception or not
132 };
133 
134 template<std::size_t TaskCount>
135 std::uint64_t exception_handling_test_case<TaskCount>::exception_mask(0);
136 
137 //! Testing exception hangling
138 //! \brief \ref requirement \ref error_guessing
139 TEST_CASE("Test exception hangling") {
140     for ( auto concurrency_level : utils::concurrency_range() ) {
141         if (concurrency_level < 2) continue;
142         oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
143 
144         exception_handling_test_case<2>::run_validate_and_reset();
145         exception_handling_test_case<3>::run_validate_and_reset();
146         exception_handling_test_case<4>::run_validate_and_reset();
147         exception_handling_test_case<5>::run_validate_and_reset();
148         exception_handling_test_case<6>::run_validate_and_reset();
149         exception_handling_test_case<7>::run_validate_and_reset();
150         exception_handling_test_case<8>::run_validate_and_reset();
151         exception_handling_test_case<9>::run_validate_and_reset();
152         exception_handling_test_case<10>::run_validate_and_reset();
153     }
154 }
155 #endif /* TBB_USE_EXCEPTIONS */
156 
157 // Cancellation support test
158 void function_to_cancel() {
159     ++g_CurExecuted;
160     Cancellator::WaitUntilReady();
161 }
162 
163 // The function is used to test cancellation
164 void simple_test_nothrow (){
165     ++g_CurExecuted;
166 }
167 
168 std::size_t g_numFunctions, g_functionToCancel;
169 
170 struct ParInvokeLauncher {
171     oneapi::tbb::task_group_context &my_ctx;
172 
173     void operator()() const {
174         void(*func_array[10])(void);
175         for (int i = 0; i <=9; ++i)
176             func_array[i] = &simple_test_nothrow;
177         func_array[g_functionToCancel] = &function_to_cancel;
178 
179         oneapi::tbb::parallel_invoke(func_array[0], func_array[1], func_array[2], func_array[3],
180             func_array[4], func_array[5], func_array[6], func_array[7], func_array[8], func_array[9], my_ctx);
181     }
182 
183     ParInvokeLauncher ( oneapi::tbb::task_group_context& ctx ) : my_ctx(ctx) {}
184 };
185 
186 //! Testing cancellation
187 //! \brief \ref requirement \ref error_guessing
188 TEST_CASE("Test cancellation") {
189     for ( auto concurrency_level : utils::concurrency_range() ) {
190         if (concurrency_level < 2) continue;
191         oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
192 
193         for ( int n = 2; n <= 10; ++n ) {
194             for ( int m = 0; m <= n - 1; ++m ) {
195                 g_numFunctions = n;
196                 g_functionToCancel = m;
197                 ResetEhGlobals();
198                 RunCancellationTest<ParInvokeLauncher, Cancellator>();
199             }
200         }
201     }
202 }
203