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