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