151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev Copyright (c) 2020-2021 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
17*b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18*b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19*b15aabb3Stbbdev #endif
20*b15aabb3Stbbdev
21*b15aabb3Stbbdev
2251c0b2f7Stbbdev #include "common/test.h"
2351c0b2f7Stbbdev #include "common/utils.h"
2451c0b2f7Stbbdev #include "common/cpu_usertime.h"
2551c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
2651c0b2f7Stbbdev #include "common/parallel_invoke_common.h"
2751c0b2f7Stbbdev #include "common/memory_usage.h"
2851c0b2f7Stbbdev
2951c0b2f7Stbbdev #include <cstddef>
3051c0b2f7Stbbdev #include <cstdint>
3151c0b2f7Stbbdev #include <atomic>
3251c0b2f7Stbbdev
3351c0b2f7Stbbdev //! \file conformance_parallel_invoke.cpp
3451c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_invoke] specification
3551c0b2f7Stbbdev
3651c0b2f7Stbbdev template<std::size_t TaskCount>
3751c0b2f7Stbbdev struct correctness_test_case {
3851c0b2f7Stbbdev static std::atomic<std::size_t> data_array[TaskCount];
3951c0b2f7Stbbdev
4051c0b2f7Stbbdev // invocation functor
4151c0b2f7Stbbdev template<std::size_t Position>
4251c0b2f7Stbbdev struct functor {
4351c0b2f7Stbbdev
4451c0b2f7Stbbdev functor() = default;
4551c0b2f7Stbbdev functor(const functor&) = delete;
4651c0b2f7Stbbdev functor& operator=(const functor&) = delete;
4751c0b2f7Stbbdev
operator ()correctness_test_case::functor4851c0b2f7Stbbdev void operator()() const {
4951c0b2f7Stbbdev REQUIRE_MESSAGE(Position < TaskCount, "Wrong structure configuration.");
5051c0b2f7Stbbdev data_array[Position]++;
5151c0b2f7Stbbdev }
5251c0b2f7Stbbdev };
5351c0b2f7Stbbdev
run_validate_and_resetcorrectness_test_case5449e08aacStbbdev static void run_validate_and_reset(oneapi::tbb::task_group_context* context_ptr) {
5551c0b2f7Stbbdev for (auto& elem : data_array)
5651c0b2f7Stbbdev elem.store(0, std::memory_order_relaxed);
5751c0b2f7Stbbdev
5851c0b2f7Stbbdev parallel_invoke_call<TaskCount, functor>::perform(context_ptr);
5951c0b2f7Stbbdev for (std::size_t i = 0; i < TaskCount; i++) {
6051c0b2f7Stbbdev REQUIRE_MESSAGE(data_array[i] == 1, "Some task was executed more than once, or was not executed.");
6151c0b2f7Stbbdev data_array[i] = 0;
6251c0b2f7Stbbdev }
6351c0b2f7Stbbdev }
6451c0b2f7Stbbdev };
6551c0b2f7Stbbdev
6651c0b2f7Stbbdev template<std::size_t TaskCount>
6751c0b2f7Stbbdev std::atomic<std::size_t> correctness_test_case<TaskCount>::data_array[TaskCount];
6851c0b2f7Stbbdev
correctness_test(oneapi::tbb::task_group_context * context_ptr=nullptr)6949e08aacStbbdev void correctness_test(oneapi::tbb::task_group_context* context_ptr = nullptr) {
7051c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
7149e08aacStbbdev oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
7251c0b2f7Stbbdev
7351c0b2f7Stbbdev correctness_test_case<2>::run_validate_and_reset(context_ptr);
7451c0b2f7Stbbdev correctness_test_case<3>::run_validate_and_reset(context_ptr);
7551c0b2f7Stbbdev correctness_test_case<4>::run_validate_and_reset(context_ptr);
7651c0b2f7Stbbdev correctness_test_case<5>::run_validate_and_reset(context_ptr);
7751c0b2f7Stbbdev correctness_test_case<6>::run_validate_and_reset(context_ptr);
7851c0b2f7Stbbdev correctness_test_case<7>::run_validate_and_reset(context_ptr);
7951c0b2f7Stbbdev correctness_test_case<8>::run_validate_and_reset(context_ptr);
8051c0b2f7Stbbdev correctness_test_case<9>::run_validate_and_reset(context_ptr);
8151c0b2f7Stbbdev correctness_test_case<10>::run_validate_and_reset(context_ptr);
8251c0b2f7Stbbdev // ...
8351c0b2f7Stbbdev correctness_test_case<50>::run_validate_and_reset(context_ptr);
8451c0b2f7Stbbdev correctness_test_case<51>::run_validate_and_reset(context_ptr);
8551c0b2f7Stbbdev correctness_test_case<52>::run_validate_and_reset(context_ptr);
8651c0b2f7Stbbdev }
8751c0b2f7Stbbdev }
8851c0b2f7Stbbdev
8951c0b2f7Stbbdev //! Testing correctness with various functors count
9051c0b2f7Stbbdev //! \brief \ref requirement \ref interface
9151c0b2f7Stbbdev TEST_CASE("Test correctness") {
9251c0b2f7Stbbdev correctness_test();
9351c0b2f7Stbbdev }
9451c0b2f7Stbbdev
9551c0b2f7Stbbdev //! Testing correctness with various functors count using task_group_context
9651c0b2f7Stbbdev //! \brief \ref requirement \ref interface
9751c0b2f7Stbbdev TEST_CASE("Test correctness using context") {
9849e08aacStbbdev oneapi::tbb::task_group_context context;
9951c0b2f7Stbbdev correctness_test(&context);
10051c0b2f7Stbbdev }
10151c0b2f7Stbbdev
10251c0b2f7Stbbdev // Exception handling support test
10351c0b2f7Stbbdev #define UTILS_EXCEPTION_HANDLING_SIMPLE_MODE 1
10451c0b2f7Stbbdev #include "common/exception_handling.h"
10551c0b2f7Stbbdev
10651c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
10751c0b2f7Stbbdev
10851c0b2f7Stbbdev template<std::size_t TaskCount>
10951c0b2f7Stbbdev struct exception_handling_test_case {
11051c0b2f7Stbbdev // invocation functor
11151c0b2f7Stbbdev template<std::size_t Position>
11251c0b2f7Stbbdev struct functor {
11351c0b2f7Stbbdev functor() = default;
11451c0b2f7Stbbdev functor(const functor&) = delete;
11551c0b2f7Stbbdev functor& operator=(const functor&) = delete;
11651c0b2f7Stbbdev
operator ()exception_handling_test_case::functor11751c0b2f7Stbbdev void operator()() const {
11851c0b2f7Stbbdev REQUIRE_MESSAGE(Position < TaskCount, "Wrong structure configuration.");
11951c0b2f7Stbbdev if (exception_mask & (1 << Position)) {
12051c0b2f7Stbbdev ThrowTestException();
12151c0b2f7Stbbdev }
12251c0b2f7Stbbdev }
12351c0b2f7Stbbdev };
12451c0b2f7Stbbdev
run_validate_and_resetexception_handling_test_case12551c0b2f7Stbbdev static void run_validate_and_reset() {
12651c0b2f7Stbbdev // Checks all permutations of the exception handling mask for the current tasks count
12751c0b2f7Stbbdev for( exception_mask = 1; exception_mask < (std::size_t(1) << TaskCount); ++exception_mask ) {
12851c0b2f7Stbbdev ResetEhGlobals();
12951c0b2f7Stbbdev TRY();
13051c0b2f7Stbbdev parallel_invoke_call<TaskCount, functor>::perform();
13151c0b2f7Stbbdev CATCH();
13251c0b2f7Stbbdev ASSERT_EXCEPTION();
13351c0b2f7Stbbdev }
13451c0b2f7Stbbdev }
13551c0b2f7Stbbdev private:
13651c0b2f7Stbbdev static std::uint64_t exception_mask; // each bit represents whether the function should throw exception or not
13751c0b2f7Stbbdev };
13851c0b2f7Stbbdev
13951c0b2f7Stbbdev template<std::size_t TaskCount>
14051c0b2f7Stbbdev std::uint64_t exception_handling_test_case<TaskCount>::exception_mask(0);
14151c0b2f7Stbbdev
14251c0b2f7Stbbdev //! Testing exception hangling
14351c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
14451c0b2f7Stbbdev TEST_CASE("Test exception hangling") {
14551c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
14651c0b2f7Stbbdev if (concurrency_level < 2) continue;
14749e08aacStbbdev oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
14851c0b2f7Stbbdev
14951c0b2f7Stbbdev exception_handling_test_case<2>::run_validate_and_reset();
15051c0b2f7Stbbdev exception_handling_test_case<3>::run_validate_and_reset();
15151c0b2f7Stbbdev exception_handling_test_case<4>::run_validate_and_reset();
15251c0b2f7Stbbdev exception_handling_test_case<5>::run_validate_and_reset();
15351c0b2f7Stbbdev exception_handling_test_case<6>::run_validate_and_reset();
15451c0b2f7Stbbdev exception_handling_test_case<7>::run_validate_and_reset();
15551c0b2f7Stbbdev exception_handling_test_case<8>::run_validate_and_reset();
15651c0b2f7Stbbdev exception_handling_test_case<9>::run_validate_and_reset();
15751c0b2f7Stbbdev exception_handling_test_case<10>::run_validate_and_reset();
15851c0b2f7Stbbdev }
15951c0b2f7Stbbdev }
16051c0b2f7Stbbdev #endif /* TBB_USE_EXCEPTIONS */
16151c0b2f7Stbbdev
16251c0b2f7Stbbdev // Cancellation support test
function_to_cancel()16351c0b2f7Stbbdev void function_to_cancel() {
16451c0b2f7Stbbdev ++g_CurExecuted;
16551c0b2f7Stbbdev Cancellator::WaitUntilReady();
16651c0b2f7Stbbdev }
16751c0b2f7Stbbdev
16851c0b2f7Stbbdev // The function is used to test cancellation
simple_test_nothrow()16951c0b2f7Stbbdev void simple_test_nothrow (){
17051c0b2f7Stbbdev ++g_CurExecuted;
17151c0b2f7Stbbdev }
17251c0b2f7Stbbdev
17351c0b2f7Stbbdev std::size_t g_numFunctions, g_functionToCancel;
17451c0b2f7Stbbdev
17551c0b2f7Stbbdev struct ParInvokeLauncher {
17649e08aacStbbdev oneapi::tbb::task_group_context &my_ctx;
17751c0b2f7Stbbdev
operator ()ParInvokeLauncher17851c0b2f7Stbbdev void operator()() const {
17951c0b2f7Stbbdev void(*func_array[10])(void);
18051c0b2f7Stbbdev for (int i = 0; i <=9; ++i)
18151c0b2f7Stbbdev func_array[i] = &simple_test_nothrow;
18251c0b2f7Stbbdev func_array[g_functionToCancel] = &function_to_cancel;
18351c0b2f7Stbbdev
18449e08aacStbbdev oneapi::tbb::parallel_invoke(func_array[0], func_array[1], func_array[2], func_array[3],
18551c0b2f7Stbbdev func_array[4], func_array[5], func_array[6], func_array[7], func_array[8], func_array[9], my_ctx);
18651c0b2f7Stbbdev }
18751c0b2f7Stbbdev
ParInvokeLauncherParInvokeLauncher18849e08aacStbbdev ParInvokeLauncher ( oneapi::tbb::task_group_context& ctx ) : my_ctx(ctx) {}
18951c0b2f7Stbbdev };
19051c0b2f7Stbbdev
19151c0b2f7Stbbdev //! Testing cancellation
19251c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
19351c0b2f7Stbbdev TEST_CASE("Test cancellation") {
19451c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
19551c0b2f7Stbbdev if (concurrency_level < 2) continue;
19649e08aacStbbdev oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
19751c0b2f7Stbbdev
19851c0b2f7Stbbdev for ( int n = 2; n <= 10; ++n ) {
19951c0b2f7Stbbdev for ( int m = 0; m <= n - 1; ++m ) {
20051c0b2f7Stbbdev g_numFunctions = n;
20151c0b2f7Stbbdev g_functionToCancel = m;
20251c0b2f7Stbbdev ResetEhGlobals();
20351c0b2f7Stbbdev RunCancellationTest<ParInvokeLauncher, Cancellator>();
20451c0b2f7Stbbdev }
20551c0b2f7Stbbdev }
20651c0b2f7Stbbdev }
20751c0b2f7Stbbdev }
208