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
1751c0b2f7Stbbdev //! \file test_global_control.cpp
1851c0b2f7Stbbdev //! \brief Test for [sched.global_control] specification
1951c0b2f7Stbbdev
2051c0b2f7Stbbdev #include "common/test.h"
2151c0b2f7Stbbdev
2251c0b2f7Stbbdev #include "common/utils.h"
2351c0b2f7Stbbdev #include "common/spin_barrier.h"
2451c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
2551c0b2f7Stbbdev
2651c0b2f7Stbbdev #include "tbb/global_control.h"
2751c0b2f7Stbbdev #include "tbb/parallel_for.h"
2851c0b2f7Stbbdev #include "tbb/task_group.h"
2951c0b2f7Stbbdev #include "tbb/task_arena.h"
3051c0b2f7Stbbdev
3151c0b2f7Stbbdev #include <cstring>
3251c0b2f7Stbbdev
3351c0b2f7Stbbdev struct task_scheduler_handle_guard {
3451c0b2f7Stbbdev tbb::task_scheduler_handle m_handle{};
3551c0b2f7Stbbdev
task_scheduler_handle_guardtask_scheduler_handle_guard3651c0b2f7Stbbdev task_scheduler_handle_guard() {
375fc0a5f6SAlex m_handle = tbb::task_scheduler_handle{tbb::attach{}};
3851c0b2f7Stbbdev }
3951c0b2f7Stbbdev
~task_scheduler_handle_guardtask_scheduler_handle_guard4051c0b2f7Stbbdev ~task_scheduler_handle_guard() {
415fc0a5f6SAlex m_handle.release();
4251c0b2f7Stbbdev }
4351c0b2f7Stbbdev
gettask_scheduler_handle_guard4451c0b2f7Stbbdev tbb::task_scheduler_handle& get() {
4551c0b2f7Stbbdev return m_handle;
4651c0b2f7Stbbdev }
4751c0b2f7Stbbdev };
4851c0b2f7Stbbdev
4951c0b2f7Stbbdev namespace TestBlockingTerminateNS {
5051c0b2f7Stbbdev
5151c0b2f7Stbbdev struct TestAutoInitBody {
operator ()TestBlockingTerminateNS::TestAutoInitBody5251c0b2f7Stbbdev void operator()( int ) const {
535fc0a5f6SAlex tbb::parallel_for( 0, 100, utils::DummyBody() );
5451c0b2f7Stbbdev }
5551c0b2f7Stbbdev };
5651c0b2f7Stbbdev
5751c0b2f7Stbbdev static std::atomic<int> gSeed;
5851c0b2f7Stbbdev static std::atomic<int> gNumSuccesses;
5951c0b2f7Stbbdev
6051c0b2f7Stbbdev class TestMultpleWaitBody {
6151c0b2f7Stbbdev bool myAutoInit;
6251c0b2f7Stbbdev public:
TestMultpleWaitBody(bool autoInit=false)6351c0b2f7Stbbdev TestMultpleWaitBody( bool autoInit = false ) : myAutoInit( autoInit ) {}
operator ()(int) const6451c0b2f7Stbbdev void operator()( int ) const {
6551c0b2f7Stbbdev task_scheduler_handle_guard init;
6651c0b2f7Stbbdev if ( !myAutoInit ) {
675fc0a5f6SAlex tbb::parallel_for(0, 10, utils::DummyBody());
6851c0b2f7Stbbdev }
6951c0b2f7Stbbdev utils::FastRandom<> rnd( ++gSeed );
7051c0b2f7Stbbdev // In case of auto init sub-tests we skip
7151c0b2f7Stbbdev // - case #4 to avoid recursion
7251c0b2f7Stbbdev // - case #5 because it is explicit initialization
7351c0b2f7Stbbdev const int numCases = myAutoInit ? 4 : 6;
7451c0b2f7Stbbdev switch ( rnd.get() % numCases ) {
7551c0b2f7Stbbdev case 0: {
7651c0b2f7Stbbdev tbb::task_arena a;
775fc0a5f6SAlex a.enqueue(utils::DummyBody() );
7851c0b2f7Stbbdev break;
7951c0b2f7Stbbdev }
8051c0b2f7Stbbdev case 1: {
8151c0b2f7Stbbdev tbb::task_group tg;
825fc0a5f6SAlex utils::DummyBody eb;
8351c0b2f7Stbbdev tg.run( eb );
8451c0b2f7Stbbdev tg.wait();
8551c0b2f7Stbbdev break;
8651c0b2f7Stbbdev }
8751c0b2f7Stbbdev case 2:
885fc0a5f6SAlex tbb::parallel_for( 0, 100, utils::DummyBody() );
8951c0b2f7Stbbdev break;
9051c0b2f7Stbbdev case 3:
9151c0b2f7Stbbdev /* do nothing */
9251c0b2f7Stbbdev break;
9351c0b2f7Stbbdev case 4:
9451c0b2f7Stbbdev // Create and join several threads with auto initialized scheduler.
9551c0b2f7Stbbdev utils::NativeParallelFor( rnd.get() % 5 + 1, TestMultpleWaitBody( true ) );
9651c0b2f7Stbbdev break;
9751c0b2f7Stbbdev case 5:
9851c0b2f7Stbbdev {
9951c0b2f7Stbbdev task_scheduler_handle_guard init2;
10051c0b2f7Stbbdev bool res = tbb::finalize( init2.get(), std::nothrow );
10151c0b2f7Stbbdev REQUIRE( !res );
10251c0b2f7Stbbdev }
10351c0b2f7Stbbdev break;
10451c0b2f7Stbbdev }
10551c0b2f7Stbbdev if ( !myAutoInit && tbb::finalize( init.get(), std::nothrow ) )
10651c0b2f7Stbbdev ++gNumSuccesses;
10751c0b2f7Stbbdev }
10851c0b2f7Stbbdev };
10951c0b2f7Stbbdev
TestMultpleWait()11051c0b2f7Stbbdev void TestMultpleWait() {
11151c0b2f7Stbbdev const int minThreads = 1;
11251c0b2f7Stbbdev const int maxThreads = 16;
11351c0b2f7Stbbdev const int numRepeats = 5;
11451c0b2f7Stbbdev // Initialize seed with different values on different machines.
11551c0b2f7Stbbdev gSeed = tbb::this_task_arena::max_concurrency();
11651c0b2f7Stbbdev for ( int repeats = 0; repeats<numRepeats; ++repeats ) {
11751c0b2f7Stbbdev for ( int threads = minThreads; threads<maxThreads; ++threads ) {
11851c0b2f7Stbbdev gNumSuccesses = 0;
11951c0b2f7Stbbdev utils::NativeParallelFor( threads, TestMultpleWaitBody() );
12051c0b2f7Stbbdev REQUIRE_MESSAGE( gNumSuccesses > 0, "At least one blocking terminate must return 'true'" );
12151c0b2f7Stbbdev }
12251c0b2f7Stbbdev }
12351c0b2f7Stbbdev }
12451c0b2f7Stbbdev
12551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
12651c0b2f7Stbbdev template <typename F>
TestException(F & f)12751c0b2f7Stbbdev void TestException( F &f ) {
12851c0b2f7Stbbdev utils::suppress_unused_warning( f );
12951c0b2f7Stbbdev bool caught = false;
13051c0b2f7Stbbdev try {
13151c0b2f7Stbbdev f();
13251c0b2f7Stbbdev REQUIRE( false );
13351c0b2f7Stbbdev }
13451c0b2f7Stbbdev catch ( const tbb::unsafe_wait& e) {
13551c0b2f7Stbbdev const char* msg = e.what();
13651c0b2f7Stbbdev REQUIRE((msg && std::strlen(msg) != 0));
13751c0b2f7Stbbdev caught = true;
13851c0b2f7Stbbdev }
13951c0b2f7Stbbdev catch ( ... ) {
14051c0b2f7Stbbdev REQUIRE( false );
14151c0b2f7Stbbdev }
14251c0b2f7Stbbdev REQUIRE( caught );
14351c0b2f7Stbbdev }
14451c0b2f7Stbbdev
14551c0b2f7Stbbdev class ExceptionTest1 {
14651c0b2f7Stbbdev task_scheduler_handle_guard tsi1;
14751c0b2f7Stbbdev int myIndex;
14851c0b2f7Stbbdev
14951c0b2f7Stbbdev public:
ExceptionTest1(int index)15051c0b2f7Stbbdev ExceptionTest1( int index ) : myIndex( index ) {}
15151c0b2f7Stbbdev
operator ()()15251c0b2f7Stbbdev void operator()() {
15351c0b2f7Stbbdev task_scheduler_handle_guard tsi2;
1545fc0a5f6SAlex tbb::parallel_for(0, 2, utils::DummyBody()); // auto-init
15551c0b2f7Stbbdev tbb::finalize((myIndex == 0 ? tsi1.get() : tsi2.get()));
15651c0b2f7Stbbdev REQUIRE_MESSAGE( false, "Blocking terminate did not throw the exception" );
15751c0b2f7Stbbdev }
15851c0b2f7Stbbdev };
15951c0b2f7Stbbdev
16051c0b2f7Stbbdev struct ExceptionTest2 {
16151c0b2f7Stbbdev class Body {
16251c0b2f7Stbbdev utils::SpinBarrier& myBarrier;
16351c0b2f7Stbbdev public:
Body(utils::SpinBarrier & barrier)16451c0b2f7Stbbdev Body( utils::SpinBarrier& barrier ) : myBarrier( barrier ) {}
operator ()(int) const16551c0b2f7Stbbdev void operator()( int ) const {
16651c0b2f7Stbbdev myBarrier.wait();
16751c0b2f7Stbbdev task_scheduler_handle_guard init;
16851c0b2f7Stbbdev tbb::finalize( init.get() );
16951c0b2f7Stbbdev REQUIRE_MESSAGE( false, "Blocking terminate did not throw the exception inside the parallel region" );
17051c0b2f7Stbbdev }
17151c0b2f7Stbbdev };
operator ()TestBlockingTerminateNS::ExceptionTest217251c0b2f7Stbbdev void operator()() {
17351c0b2f7Stbbdev const int numThreads = 4;
17451c0b2f7Stbbdev tbb::global_control init(tbb::global_control::max_allowed_parallelism, numThreads);
175478de5b1Stbbdev tbb::task_arena a(numThreads);
176478de5b1Stbbdev a.execute([&] {
17751c0b2f7Stbbdev utils::SpinBarrier barrier(numThreads);
17851c0b2f7Stbbdev tbb::parallel_for(0, numThreads, Body(barrier));
17951c0b2f7Stbbdev REQUIRE_MESSAGE(false, "Parallel loop did not throw the exception");
180478de5b1Stbbdev });
18151c0b2f7Stbbdev }
18251c0b2f7Stbbdev };
18351c0b2f7Stbbdev
TestExceptions()18451c0b2f7Stbbdev void TestExceptions() {
1858b6f831cStbbdev ExceptionTest1 Test1(0);
18651c0b2f7Stbbdev TestException( Test1 );
1878b6f831cStbbdev ExceptionTest1 Test2(1);
18851c0b2f7Stbbdev TestException( Test2 );
1898b6f831cStbbdev if (utils::get_platform_max_threads() > 1) {
1908b6f831cStbbdev // TODO: Fix the arena leak issue on single threaded machine
1918b6f831cStbbdev // (see https://github.com/oneapi-src/oneTBB/issues/396)
19251c0b2f7Stbbdev ExceptionTest2 Test3;
19351c0b2f7Stbbdev TestException(Test3);
19451c0b2f7Stbbdev }
1958b6f831cStbbdev }
19651c0b2f7Stbbdev
19751c0b2f7Stbbdev #endif /* TBB_USE_EXCEPTIONS */
19851c0b2f7Stbbdev
19951c0b2f7Stbbdev } // namespace TestBlockingTerminateNS
20051c0b2f7Stbbdev
TestTerminationAndAutoinit(bool autoinit)20151c0b2f7Stbbdev void TestTerminationAndAutoinit(bool autoinit) {
20251c0b2f7Stbbdev task_scheduler_handle_guard ctl1;
20351c0b2f7Stbbdev task_scheduler_handle_guard ctl2;
20451c0b2f7Stbbdev
20551c0b2f7Stbbdev if (autoinit) {
2065fc0a5f6SAlex tbb::parallel_for(0, 10, utils::DummyBody());
20751c0b2f7Stbbdev }
20851c0b2f7Stbbdev bool res1 = tbb::finalize(ctl1.get(), std::nothrow);
20951c0b2f7Stbbdev if (autoinit) {
21051c0b2f7Stbbdev REQUIRE(!res1);
21151c0b2f7Stbbdev } else {
21251c0b2f7Stbbdev REQUIRE(res1);
21351c0b2f7Stbbdev }
21451c0b2f7Stbbdev bool res2 = tbb::finalize(ctl2.get(), std::nothrow);
21551c0b2f7Stbbdev REQUIRE(res2);
21651c0b2f7Stbbdev }
21751c0b2f7Stbbdev
2189924f9e8SIlya Isaev //! Check no reference leak for an external thread
2199924f9e8SIlya Isaev //! \brief \ref regression \ref error_guessing
2209924f9e8SIlya Isaev TEST_CASE("test decrease reference") {
2215fc0a5f6SAlex tbb::task_scheduler_handle handle = tbb::task_scheduler_handle{tbb::attach{}};
2229924f9e8SIlya Isaev
__anon8f1bc23b0302(int) 2239924f9e8SIlya Isaev std::thread thr([] { tbb::parallel_for(0, 1, [](int) {}); } );
2249924f9e8SIlya Isaev thr.join();
2259924f9e8SIlya Isaev
2269924f9e8SIlya Isaev REQUIRE(tbb::finalize(handle, std::nothrow));
2279924f9e8SIlya Isaev }
2289924f9e8SIlya Isaev
2295fc0a5f6SAlex //! Testing lifetime control
2305fc0a5f6SAlex //! \brief \ref error_guessing
2315fc0a5f6SAlex TEST_CASE("prolong lifetime auto init") {
23251c0b2f7Stbbdev TestTerminationAndAutoinit(false);
23351c0b2f7Stbbdev TestTerminationAndAutoinit(true);
23451c0b2f7Stbbdev }
23551c0b2f7Stbbdev
23651c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
23751c0b2f7Stbbdev //! Testing lifetime control advanced
2385fc0a5f6SAlex //! \brief \ref error_guessing
23951c0b2f7Stbbdev TEST_CASE("prolong lifetime advanced") {
240*c4a799dfSJhaShweta1 // Exceptions test leaves auto-initialized scheduler after,
24151c0b2f7Stbbdev // because all blocking terminate calls are inside the parallel region,
24251c0b2f7Stbbdev // thus resulting in false termination result.
24351c0b2f7Stbbdev utils::NativeParallelFor(1,
__anon8f1bc23b0402(int) 24451c0b2f7Stbbdev [&](int) { TestBlockingTerminateNS::TestExceptions(); });
24551c0b2f7Stbbdev }
24651c0b2f7Stbbdev #endif
24751c0b2f7Stbbdev
24851c0b2f7Stbbdev //! Testing multiple wait
2495fc0a5f6SAlex //! \brief \ref error_guessing
25051c0b2f7Stbbdev TEST_CASE("prolong lifetime multiple wait") {
25151c0b2f7Stbbdev TestBlockingTerminateNS::TestMultpleWait();
25251c0b2f7Stbbdev }
25351c0b2f7Stbbdev
254c4568449SPavel Kumbrasev //! \brief \ref regression
255c4568449SPavel Kumbrasev TEST_CASE("test concurrent task_scheduler_handle destruction") {
256c4568449SPavel Kumbrasev std::atomic<bool> stop{ false };
__anon8f1bc23b0502null257c4568449SPavel Kumbrasev std::thread thr1([&] {
258c4568449SPavel Kumbrasev while (!stop) {
259c4568449SPavel Kumbrasev auto h = tbb::task_scheduler_handle{ tbb::attach{} };
260c4568449SPavel Kumbrasev tbb::finalize(h, std::nothrow_t{});
261c4568449SPavel Kumbrasev }
262c4568449SPavel Kumbrasev });
263c4568449SPavel Kumbrasev
264c4568449SPavel Kumbrasev for (int i = 0; i < 1000; ++i) {
__anon8f1bc23b0602null265c4568449SPavel Kumbrasev std::thread thr2([] {
266c4568449SPavel Kumbrasev tbb::parallel_for(0, 1, [](int) {});
267c4568449SPavel Kumbrasev });
268c4568449SPavel Kumbrasev thr2.join();
269c4568449SPavel Kumbrasev }
270c4568449SPavel Kumbrasev stop = true;
271c4568449SPavel Kumbrasev thr1.join();
272c4568449SPavel Kumbrasev }
273