151c0b2f7Stbbdev /*
2c4568449SPavel Kumbrasev     Copyright (c) 2005-2023 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 #include "common/test.h"
1751c0b2f7Stbbdev #include "common/concurrency_tracker.h"
1851c0b2f7Stbbdev #include "common/spin_barrier.h"
1951c0b2f7Stbbdev #include "common/utils.h"
2051c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
2151c0b2f7Stbbdev 
2249e08aacStbbdev #include "oneapi/tbb/global_control.h"
2349e08aacStbbdev #include "oneapi/tbb/parallel_for.h"
2451c0b2f7Stbbdev 
2551c0b2f7Stbbdev #include <limits.h>
2651c0b2f7Stbbdev #include <thread>
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev //! \file conformance_global_control.cpp
2951c0b2f7Stbbdev //! \brief Test for [sched.global_control] specification
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev const std::size_t MB = 1024*1024;
3251c0b2f7Stbbdev 
TestStackSizeSimpleControl()3351c0b2f7Stbbdev void TestStackSizeSimpleControl() {
3449e08aacStbbdev     oneapi::tbb::global_control s0(oneapi::tbb::global_control::thread_stack_size, 1*MB);
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev     {
3749e08aacStbbdev         oneapi::tbb::global_control s1(oneapi::tbb::global_control::thread_stack_size, 8*MB);
3851c0b2f7Stbbdev 
3949e08aacStbbdev         CHECK(8*MB == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size));
4051c0b2f7Stbbdev     }
4149e08aacStbbdev     CHECK(1*MB == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size));
4251c0b2f7Stbbdev }
4351c0b2f7Stbbdev 
4451c0b2f7Stbbdev struct StackSizeRun : utils::NoAssign {
4551c0b2f7Stbbdev 
4651c0b2f7Stbbdev     int num_threads;
4751c0b2f7Stbbdev     utils::SpinBarrier *barr1, *barr2;
4851c0b2f7Stbbdev 
StackSizeRunStackSizeRun4951c0b2f7Stbbdev     StackSizeRun(int threads, utils::SpinBarrier *b1, utils::SpinBarrier *b2) :
5051c0b2f7Stbbdev         num_threads(threads), barr1(b1), barr2(b2) {}
operator ()StackSizeRun5151c0b2f7Stbbdev     void operator()( int id ) const {
5249e08aacStbbdev         oneapi::tbb::global_control s1(oneapi::tbb::global_control::thread_stack_size, (1+id)*MB);
5351c0b2f7Stbbdev 
54b15aabb3Stbbdev         barr1->wait();
5551c0b2f7Stbbdev 
5649e08aacStbbdev         REQUIRE(num_threads*MB == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size));
57b15aabb3Stbbdev         barr2->wait();
5851c0b2f7Stbbdev     }
5951c0b2f7Stbbdev };
6051c0b2f7Stbbdev 
TestStackSizeThreadsControl()6151c0b2f7Stbbdev void TestStackSizeThreadsControl() {
6251c0b2f7Stbbdev     int threads = 4;
6351c0b2f7Stbbdev     utils::SpinBarrier barr1(threads), barr2(threads);
6451c0b2f7Stbbdev     utils::NativeParallelFor( threads, StackSizeRun(threads, &barr1, &barr2) );
6551c0b2f7Stbbdev }
6651c0b2f7Stbbdev 
RunWorkersLimited(size_t parallelism)67c4568449SPavel Kumbrasev void RunWorkersLimited(size_t parallelism)
6851c0b2f7Stbbdev {
6949e08aacStbbdev     oneapi::tbb::global_control s(oneapi::tbb::global_control::max_allowed_parallelism, parallelism);
70c4568449SPavel Kumbrasev     // TODO: consider better testing approach
71c4568449SPavel Kumbrasev     // Sleep is required because after destruction global_control on the previous iteration,
72c4568449SPavel Kumbrasev     // it recalls the maximum concurrency and excessive worker threads might populate the arena.
73c4568449SPavel Kumbrasev     // So, we need to wait when arena becomes empty but it is unreliable and might sporadically fail.
74c4568449SPavel Kumbrasev     utils::Sleep(100);
7551c0b2f7Stbbdev     const std::size_t expected_threads = (utils::get_platform_max_threads()==1)? 1 : parallelism;
7651c0b2f7Stbbdev     utils::ExactConcurrencyLevel::check(expected_threads);
7751c0b2f7Stbbdev }
7851c0b2f7Stbbdev 
TestWorkersConstraints()7951c0b2f7Stbbdev void TestWorkersConstraints()
8051c0b2f7Stbbdev {
8151c0b2f7Stbbdev     const size_t max_parallelism =
8249e08aacStbbdev         oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism);
8351c0b2f7Stbbdev     if (max_parallelism > 3) {
8449e08aacStbbdev         oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, max_parallelism-1);
8551c0b2f7Stbbdev         CHECK_MESSAGE(max_parallelism-1 ==
8649e08aacStbbdev                oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism),
8751c0b2f7Stbbdev                "Allowed parallelism must be decreasable.");
8849e08aacStbbdev         oneapi::tbb::global_control c1(oneapi::tbb::global_control::max_allowed_parallelism, max_parallelism-2);
8951c0b2f7Stbbdev         CHECK_MESSAGE(max_parallelism-2 ==
9049e08aacStbbdev                oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism),
9151c0b2f7Stbbdev                "Allowed parallelism must be decreasable.");
9251c0b2f7Stbbdev     }
9351c0b2f7Stbbdev     const size_t limit_par = utils::min(max_parallelism, 4U);
9451c0b2f7Stbbdev     // check that constrains are really met
9551c0b2f7Stbbdev     for (size_t num=2; num<limit_par; num++)
96c4568449SPavel Kumbrasev         RunWorkersLimited(num);
9751c0b2f7Stbbdev     for (size_t num=limit_par; num>1; num--)
98c4568449SPavel Kumbrasev         RunWorkersLimited(num);
9951c0b2f7Stbbdev }
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev struct SetUseRun: utils::NoAssign {
10251c0b2f7Stbbdev     utils::SpinBarrier &barr;
10351c0b2f7Stbbdev 
SetUseRunSetUseRun10451c0b2f7Stbbdev     SetUseRun(utils::SpinBarrier& b) : barr(b) {}
operator ()SetUseRun10551c0b2f7Stbbdev     void operator()( int id ) const {
10651c0b2f7Stbbdev         if (id == 0) {
10751c0b2f7Stbbdev             for (int i=0; i<10; i++) {
108b15aabb3Stbbdev                 oneapi::tbb::parallel_for(0, 1000, utils::DummyBody(10), oneapi::tbb::simple_partitioner());
109b15aabb3Stbbdev                 barr.wait();
11051c0b2f7Stbbdev             }
11151c0b2f7Stbbdev         } else {
11251c0b2f7Stbbdev             for (int i=0; i<10; i++) {
11349e08aacStbbdev                 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 8);
114b15aabb3Stbbdev                 barr.wait();
11551c0b2f7Stbbdev             }
11651c0b2f7Stbbdev         }
11751c0b2f7Stbbdev     }
11851c0b2f7Stbbdev };
11951c0b2f7Stbbdev 
TestConcurrentSetUseConcurrency()12051c0b2f7Stbbdev void TestConcurrentSetUseConcurrency()
12151c0b2f7Stbbdev {
12251c0b2f7Stbbdev     utils::SpinBarrier barr(2);
12351c0b2f7Stbbdev     NativeParallelFor( 2, SetUseRun(barr) );
12451c0b2f7Stbbdev }
12551c0b2f7Stbbdev 
12651c0b2f7Stbbdev // check number of workers after autoinitialization
TestAutoInit()12751c0b2f7Stbbdev void TestAutoInit()
12851c0b2f7Stbbdev {
12951c0b2f7Stbbdev     const size_t max_parallelism =
13049e08aacStbbdev         oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism);
13151c0b2f7Stbbdev     const unsigned expected_threads = utils::get_platform_max_threads()==1?
13251c0b2f7Stbbdev         1 : (unsigned)max_parallelism;
13351c0b2f7Stbbdev     utils::ExactConcurrencyLevel::check(expected_threads);
13449e08aacStbbdev     CHECK_MESSAGE(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism)
13551c0b2f7Stbbdev            == max_parallelism, "max_allowed_parallelism must not be changed after auto init");
13651c0b2f7Stbbdev     if (max_parallelism > 2) {
13751c0b2f7Stbbdev         // after autoinit it's possible to decrease workers number
13849e08aacStbbdev         oneapi::tbb::global_control s(oneapi::tbb::global_control::max_allowed_parallelism, max_parallelism-1);
139c4568449SPavel Kumbrasev         // TODO: consider better testing approach
140c4568449SPavel Kumbrasev         // Sleep is required because after previous concurrency check, the arena is still populated with workers.
141c4568449SPavel Kumbrasev         // So, we need to wait when arena becomes empty but it is unreliable and might sporadically fail.
142c4568449SPavel Kumbrasev         utils::Sleep(100);
14351c0b2f7Stbbdev         utils::ExactConcurrencyLevel::check(max_parallelism-1);
14451c0b2f7Stbbdev     }
14551c0b2f7Stbbdev }
14651c0b2f7Stbbdev 
14751c0b2f7Stbbdev class TestMultipleControlsRun {
14851c0b2f7Stbbdev     utils::SpinBarrier &barrier;
14951c0b2f7Stbbdev public:
TestMultipleControlsRun(utils::SpinBarrier & b)15051c0b2f7Stbbdev     TestMultipleControlsRun(utils::SpinBarrier& b) : barrier(b) {}
operator ()(int id) const15151c0b2f7Stbbdev     void operator()( int id ) const {
15251c0b2f7Stbbdev         barrier.wait();
15351c0b2f7Stbbdev         if (id) {
15451c0b2f7Stbbdev             {
15549e08aacStbbdev                 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 1);
15651c0b2f7Stbbdev                 utils::ExactConcurrencyLevel::check(1);
15751c0b2f7Stbbdev                 barrier.wait();
15851c0b2f7Stbbdev             }
15951c0b2f7Stbbdev             utils::ExactConcurrencyLevel::check(1);
16051c0b2f7Stbbdev             barrier.wait();
16151c0b2f7Stbbdev             {
16249e08aacStbbdev                 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 2);
16351c0b2f7Stbbdev                 utils::ExactConcurrencyLevel::check(1);
16451c0b2f7Stbbdev                 barrier.wait();
16551c0b2f7Stbbdev                 utils::ExactConcurrencyLevel::check(2);
16651c0b2f7Stbbdev                 barrier.wait();
16751c0b2f7Stbbdev             }
16851c0b2f7Stbbdev         } else {
16951c0b2f7Stbbdev             {
17051c0b2f7Stbbdev                 utils::ExactConcurrencyLevel::check(1);
17149e08aacStbbdev                 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 1);
17251c0b2f7Stbbdev                 barrier.wait();
17351c0b2f7Stbbdev                 utils::ExactConcurrencyLevel::check(1);
17451c0b2f7Stbbdev                 barrier.wait();
17551c0b2f7Stbbdev                 utils::ExactConcurrencyLevel::check(1);
17651c0b2f7Stbbdev                 barrier.wait();
17751c0b2f7Stbbdev             }
17851c0b2f7Stbbdev             utils::ExactConcurrencyLevel::check(2);
17951c0b2f7Stbbdev             barrier.wait();
18051c0b2f7Stbbdev         }
18151c0b2f7Stbbdev     }
18251c0b2f7Stbbdev };
18351c0b2f7Stbbdev 
18451c0b2f7Stbbdev // test that global controls from different thread with overlapping lifetime
18551c0b2f7Stbbdev // still keep parallelism under control
TestMultipleControls()18651c0b2f7Stbbdev void TestMultipleControls()
18751c0b2f7Stbbdev {
18851c0b2f7Stbbdev     utils::SpinBarrier barrier(2);
18951c0b2f7Stbbdev     utils::NativeParallelFor( 2, TestMultipleControlsRun(barrier) );
19051c0b2f7Stbbdev }
19151c0b2f7Stbbdev 
19251c0b2f7Stbbdev #if !(__TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00))
19351c0b2f7Stbbdev //! Testing setting stack size
19451c0b2f7Stbbdev //! \brief \ref interface \ref requirement
19551c0b2f7Stbbdev TEST_CASE("setting stack size") {
19649e08aacStbbdev     std::size_t default_ss = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size);
19751c0b2f7Stbbdev     CHECK(default_ss > 0);
19851c0b2f7Stbbdev     TestStackSizeSimpleControl();
19951c0b2f7Stbbdev     TestStackSizeThreadsControl();
20049e08aacStbbdev     CHECK(default_ss == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size));
20151c0b2f7Stbbdev }
20251c0b2f7Stbbdev #endif
20351c0b2f7Stbbdev 
20451c0b2f7Stbbdev //! Testing setting max number of threads
20551c0b2f7Stbbdev //! \brief \ref interface \ref requirement
20651c0b2f7Stbbdev TEST_CASE("setting max number of threads") {
20751c0b2f7Stbbdev     TestWorkersConstraints();
208c4568449SPavel Kumbrasev }
209c4568449SPavel Kumbrasev //! Testing concurrenct setting concurrency
210c4568449SPavel Kumbrasev //! \brief \ref interface \ref requirement
211c4568449SPavel Kumbrasev TEST_CASE("concurrenct setting concurrency") {
21251c0b2f7Stbbdev     TestConcurrentSetUseConcurrency();
213c4568449SPavel Kumbrasev }
214c4568449SPavel Kumbrasev 
215c4568449SPavel Kumbrasev //! Testing auto initialization
216c4568449SPavel Kumbrasev //! \brief \ref interface \ref requirement
217c4568449SPavel Kumbrasev TEST_CASE("auto initialization") {
21851c0b2f7Stbbdev     TestAutoInit();
21951c0b2f7Stbbdev }
22051c0b2f7Stbbdev 
22151c0b2f7Stbbdev //! Test terminate_on_exception default value
22251c0b2f7Stbbdev //! \brief \ref interface \ref requirement
22351c0b2f7Stbbdev TEST_CASE("terminate_on_exception: default") {
22449e08aacStbbdev     std::size_t default_toe = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception);
22551c0b2f7Stbbdev     CHECK(default_toe == 0);
22651c0b2f7Stbbdev }
22751c0b2f7Stbbdev 
22851c0b2f7Stbbdev //! Test terminate_on_exception in a nested case
22951c0b2f7Stbbdev //! \brief \ref interface \ref requirement
23051c0b2f7Stbbdev TEST_CASE("terminate_on_exception: nested") {
23149e08aacStbbdev     oneapi::tbb::global_control* c0;
23251c0b2f7Stbbdev     {
23349e08aacStbbdev         oneapi::tbb::global_control c1(oneapi::tbb::global_control::terminate_on_exception, 1);
23449e08aacStbbdev         CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 1);
23551c0b2f7Stbbdev         {
23649e08aacStbbdev             oneapi::tbb::global_control c2(oneapi::tbb::global_control::terminate_on_exception, 0);
23749e08aacStbbdev             CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 1);
23851c0b2f7Stbbdev         }
23949e08aacStbbdev         CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 1);
24049e08aacStbbdev         c0 = new oneapi::tbb::global_control(oneapi::tbb::global_control::terminate_on_exception, 0);
24151c0b2f7Stbbdev     }
24249e08aacStbbdev     CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 0);
24351c0b2f7Stbbdev     delete c0;
24451c0b2f7Stbbdev }
24551c0b2f7Stbbdev 
2465fc0a5f6SAlex //! Testing setting the same value but different objects
2475fc0a5f6SAlex //! \brief \ref interface \ref error_guessing
2485fc0a5f6SAlex TEST_CASE("setting same value") {
2495fc0a5f6SAlex     const std::size_t value = 2;
2505fc0a5f6SAlex 
2515fc0a5f6SAlex     oneapi::tbb::global_control* ctl1 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value);
2525fc0a5f6SAlex     oneapi::tbb::global_control* ctl2 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value);
2535fc0a5f6SAlex 
2545fc0a5f6SAlex     std::size_t active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism);
2555fc0a5f6SAlex     REQUIRE(active == value);
2565fc0a5f6SAlex     delete ctl2;
2575fc0a5f6SAlex 
2585fc0a5f6SAlex     active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism);
2595fc0a5f6SAlex     REQUIRE_MESSAGE(active == value, "Active value should not change, because of value duplication");
2605fc0a5f6SAlex     delete ctl1;
2615fc0a5f6SAlex }
2625fc0a5f6SAlex 
2635fc0a5f6SAlex //! Testing lifetime control conformance
2645fc0a5f6SAlex //! \brief \ref interface \ref requirement
2655fc0a5f6SAlex TEST_CASE("prolong lifetime simple") {
2665fc0a5f6SAlex     tbb::task_scheduler_handle hdl1{ tbb::attach{} };
2675fc0a5f6SAlex     {
2685fc0a5f6SAlex         tbb::parallel_for(0, 10, utils::DummyBody());
2695fc0a5f6SAlex 
2705fc0a5f6SAlex         tbb::task_scheduler_handle hdl2;
2715fc0a5f6SAlex         hdl2 = tbb::task_scheduler_handle{ tbb::attach{} };
2725fc0a5f6SAlex         hdl2.release();
2735fc0a5f6SAlex     }
2745fc0a5f6SAlex     bool ok = tbb::finalize(hdl1, std::nothrow);
2755fc0a5f6SAlex     REQUIRE(ok);
2765fc0a5f6SAlex }
2775fc0a5f6SAlex 
2785fc0a5f6SAlex //! Testing handle check for emptiness
2795fc0a5f6SAlex //! \brief \ref interface \ref requirement
2805fc0a5f6SAlex TEST_CASE("null handle check") {
2815fc0a5f6SAlex     tbb::task_scheduler_handle hndl;
2825fc0a5f6SAlex     REQUIRE_FALSE(hndl);
2835fc0a5f6SAlex }
2845fc0a5f6SAlex 
2855fc0a5f6SAlex //! Testing handle check for emptiness
2865fc0a5f6SAlex //! \brief \ref interface \ref requirement
2875fc0a5f6SAlex TEST_CASE("null handle check 2") {
2885fc0a5f6SAlex     tbb::task_scheduler_handle hndl{ tbb::attach{} };
2895fc0a5f6SAlex     bool not_empty = (bool)hndl;
2905fc0a5f6SAlex 
2915fc0a5f6SAlex     tbb::finalize(hndl, std::nothrow);
2925fc0a5f6SAlex 
2935fc0a5f6SAlex     REQUIRE(not_empty);
2945fc0a5f6SAlex     REQUIRE_FALSE(hndl);
2955fc0a5f6SAlex }
2965fc0a5f6SAlex 
2975fc0a5f6SAlex //! Testing handle check for emptiness
2985fc0a5f6SAlex //! \brief \ref interface \ref requirement
2995fc0a5f6SAlex TEST_CASE("null handle check 3") {
3005fc0a5f6SAlex     tbb::task_scheduler_handle handle1{ tbb::attach{} };
3015fc0a5f6SAlex     tbb::task_scheduler_handle handle2(std::move(handle1));
3025fc0a5f6SAlex 
3035fc0a5f6SAlex     bool handle1_empty = !handle1;
3045fc0a5f6SAlex     bool handle2_not_empty = (bool)handle2;
3055fc0a5f6SAlex 
3065fc0a5f6SAlex     tbb::finalize(handle2, std::nothrow);
3075fc0a5f6SAlex 
3085fc0a5f6SAlex     REQUIRE(handle1_empty);
3095fc0a5f6SAlex     REQUIRE(handle2_not_empty);
3105fc0a5f6SAlex }
3115fc0a5f6SAlex 
3125fc0a5f6SAlex //! Testing  task_scheduler_handle is created on one thread and destroyed on another.
3135fc0a5f6SAlex //! \brief \ref interface \ref requirement
3145fc0a5f6SAlex TEST_CASE("cross thread 1") {
3155fc0a5f6SAlex     // created task_scheduler_handle, parallel_for on another thread - finalize
3165fc0a5f6SAlex     tbb::task_scheduler_handle handle{ tbb::attach{} };
__anon3ebcafb90102(int) 3175fc0a5f6SAlex     utils::NativeParallelFor(1, [&](int) {
3185fc0a5f6SAlex         tbb::parallel_for(0, 10, utils::DummyBody());
3195fc0a5f6SAlex         bool res = tbb::finalize(handle, std::nothrow);
3205fc0a5f6SAlex         REQUIRE(res);
3215fc0a5f6SAlex     });
3225fc0a5f6SAlex }
3235fc0a5f6SAlex 
3245fc0a5f6SAlex //! Testing  task_scheduler_handle is created on one thread and destroyed on another.
3255fc0a5f6SAlex //! \brief \ref interface \ref requirement
3265fc0a5f6SAlex TEST_CASE("cross thread 2") {
3275fc0a5f6SAlex     // created task_scheduler_handle, called parallel_for on this thread, killed the thread - and finalize on another thread
3285fc0a5f6SAlex     tbb::task_scheduler_handle handle;
__anon3ebcafb90202(int) 3295fc0a5f6SAlex     utils::NativeParallelFor(1, [&](int) {
3305fc0a5f6SAlex         handle = tbb::task_scheduler_handle{ tbb::attach{} };
3315fc0a5f6SAlex         tbb::parallel_for(0, 10, utils::DummyBody());
3325fc0a5f6SAlex     });
3335fc0a5f6SAlex     bool res = tbb::finalize(handle, std::nothrow);
3345fc0a5f6SAlex     REQUIRE(res);
3355fc0a5f6SAlex }
3365fc0a5f6SAlex 
3375fc0a5f6SAlex //! Testing multiple wait
3385fc0a5f6SAlex //! \brief \ref interface \ref requirement
3395fc0a5f6SAlex TEST_CASE("simple prolong lifetime 3") {
3405fc0a5f6SAlex     // Parallel region
3415fc0a5f6SAlex     tbb::parallel_for(0, 10, utils::DummyBody());
3425fc0a5f6SAlex     // Termination
3435fc0a5f6SAlex     tbb::task_scheduler_handle handle = tbb::task_scheduler_handle{ tbb::attach{} };
3445fc0a5f6SAlex     bool res = tbb::finalize(handle, std::nothrow);
3455fc0a5f6SAlex     REQUIRE(res);
3465fc0a5f6SAlex     // New parallel region
3475fc0a5f6SAlex     tbb::parallel_for(0, 10, utils::DummyBody());
3485fc0a5f6SAlex }
3495fc0a5f6SAlex 
35051c0b2f7Stbbdev // The test cannot work correctly with statically linked runtime.
3519e15720bStbbdev // TODO: investigate a failure in debug with MSVC
352*7cee2251SJhaShweta1 #if (!_MSC_VER || (defined(_DLL) && !defined(_DEBUG))) && !EMSCRIPTEN
35351c0b2f7Stbbdev #include <csetjmp>
35451c0b2f7Stbbdev 
35551c0b2f7Stbbdev // Overall, the test case is not safe because the dtors might not be called during long jump.
35651c0b2f7Stbbdev // Therefore, it makes sense to run the test case after all other test cases.
35751c0b2f7Stbbdev //! Test terminate_on_exception behavior
35851c0b2f7Stbbdev //! \brief \ref interface \ref requirement
35951c0b2f7Stbbdev TEST_CASE("terminate_on_exception: enabled") {
36049e08aacStbbdev     oneapi::tbb::global_control c(oneapi::tbb::global_control::terminate_on_exception, 1);
36151c0b2f7Stbbdev     static bool terminate_handler_called;
36251c0b2f7Stbbdev     terminate_handler_called = false;
36351c0b2f7Stbbdev 
36451c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
36551c0b2f7Stbbdev     try {
36651c0b2f7Stbbdev #endif
36751c0b2f7Stbbdev         static std::jmp_buf buffer;
__anon3ebcafb90302null36851c0b2f7Stbbdev         std::terminate_handler prev = std::set_terminate([] {
36951c0b2f7Stbbdev             CHECK(!terminate_handler_called);
37051c0b2f7Stbbdev             terminate_handler_called = true;
37151c0b2f7Stbbdev             std::longjmp(buffer, 1);
37251c0b2f7Stbbdev         });
37351c0b2f7Stbbdev #if _MSC_VER
37451c0b2f7Stbbdev #pragma warning(push)
37551c0b2f7Stbbdev #pragma warning(disable:4611) // interaction between '_setjmp' and C++ object destruction is non - portable
37651c0b2f7Stbbdev #endif
37751c0b2f7Stbbdev         SUBCASE("internal exception") {
37851c0b2f7Stbbdev             if (setjmp(buffer) == 0) {
__anon3ebcafb90402(int) 37949e08aacStbbdev                 oneapi::tbb::parallel_for(0, 1, -1, [](int) {});
38051c0b2f7Stbbdev                 FAIL("Unreachable code");
38151c0b2f7Stbbdev             }
38251c0b2f7Stbbdev         }
38351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
38451c0b2f7Stbbdev         SUBCASE("user exception") {
38551c0b2f7Stbbdev             if (setjmp(buffer) == 0) {
__anon3ebcafb90502(int) 38649e08aacStbbdev                 oneapi::tbb::parallel_for(0, 1, [](int) {
38751c0b2f7Stbbdev                     volatile bool suppress_unreachable_code_warning = true;
38851c0b2f7Stbbdev                     if (suppress_unreachable_code_warning) {
38951c0b2f7Stbbdev                         throw std::exception();
39051c0b2f7Stbbdev                     }
39151c0b2f7Stbbdev                 });
39251c0b2f7Stbbdev                 FAIL("Unreachable code");
39351c0b2f7Stbbdev             }
39451c0b2f7Stbbdev         }
39551c0b2f7Stbbdev #endif
39651c0b2f7Stbbdev #if _MSC_VER
39751c0b2f7Stbbdev #pragma warning(pop)
39851c0b2f7Stbbdev #endif
39951c0b2f7Stbbdev         std::set_terminate(prev);
40051c0b2f7Stbbdev         terminate_handler_called = true;
40151c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
40251c0b2f7Stbbdev     } catch (...) {
40351c0b2f7Stbbdev         FAIL("The exception is not expected");
40451c0b2f7Stbbdev     }
40551c0b2f7Stbbdev #endif
40651c0b2f7Stbbdev     CHECK(terminate_handler_called);
40751c0b2f7Stbbdev }
40851c0b2f7Stbbdev #endif
409