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