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 48 void operator()() const { 49 REQUIRE_MESSAGE(Position < TaskCount, "Wrong structure configuration."); 50 data_array[Position]++; 51 } 52 }; 53 54 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 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 117 void operator()() const { 118 REQUIRE_MESSAGE(Position < TaskCount, "Wrong structure configuration."); 119 if (exception_mask & (1 << Position)) { 120 ThrowTestException(); 121 } 122 } 123 }; 124 125 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 163 void function_to_cancel() { 164 ++g_CurExecuted; 165 Cancellator::WaitUntilReady(); 166 } 167 168 // The function is used to test cancellation 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 178 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 188 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