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