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