151c0b2f7Stbbdev /* 213f9f32bSSergey Zheltov Copyright (c) 2005-2022 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 #include "common/test.h" 1851c0b2f7Stbbdev #include "common/utils.h" 1951c0b2f7Stbbdev 2049e08aacStbbdev #include "oneapi/tbb/task_arena.h" 2149e08aacStbbdev #include "oneapi/tbb/task_scheduler_observer.h" 2249e08aacStbbdev #include "oneapi/tbb/enumerable_thread_specific.h" 2349e08aacStbbdev #include "oneapi/tbb/parallel_for.h" 2451c0b2f7Stbbdev 2551c0b2f7Stbbdev //! \file conformance_task_arena.cpp 2651c0b2f7Stbbdev //! \brief Test for [scheduler.task_arena scheduler.task_scheduler_observer] specification 2751c0b2f7Stbbdev 28c6045a4fSPavel // This test requires TBB in an uninitialized state 29c6045a4fSPavel //! Test for uninitilized arena 30c6045a4fSPavel //! \brief \ref requirement \ref interface 31c6045a4fSPavel TEST_CASE("Test current_thread_index") { 32c6045a4fSPavel REQUIRE_MESSAGE((tbb::this_task_arena::current_thread_index() == tbb::task_arena::not_initialized), "TBB was initialized state"); 33c6045a4fSPavel } 34c6045a4fSPavel 3551c0b2f7Stbbdev //! Test task arena interfaces 3651c0b2f7Stbbdev //! \brief \ref requirement \ref interface 3751c0b2f7Stbbdev TEST_CASE("Arena interfaces") { 3851c0b2f7Stbbdev //! Initialization interfaces 3949e08aacStbbdev oneapi::tbb::task_arena a(1,1); a.initialize(); 40b15aabb3Stbbdev std::atomic<bool> done{ false }; 4151c0b2f7Stbbdev //! Enqueue interface __anon3e0b77410102null42b15aabb3Stbbdev a.enqueue([&done] { 43b15aabb3Stbbdev CHECK(oneapi::tbb::this_task_arena::max_concurrency() == 2); 44b15aabb3Stbbdev done = true; 45b15aabb3Stbbdev }); 4651c0b2f7Stbbdev //! Execute interface __anon3e0b77410202null4751c0b2f7Stbbdev a.execute([&] { 4849e08aacStbbdev //! oneapi::tbb::this_task_arena interfaces 4949e08aacStbbdev CHECK(oneapi::tbb::this_task_arena::current_thread_index() >= 0); 5051c0b2f7Stbbdev //! Attach interface 515d4a4acfSIvan Kochin oneapi::tbb::task_arena attached_arena{oneapi::tbb::task_arena::attach()}; 5251c0b2f7Stbbdev CHECK(attached_arena.is_active()); 531eaccf7aSAlex oneapi::tbb::task_arena attached_arena2{oneapi::tbb::attach()}; 541eaccf7aSAlex CHECK(attached_arena2.is_active()); 5551c0b2f7Stbbdev }); 56b15aabb3Stbbdev while (!done) { 57b15aabb3Stbbdev utils::yield(); 58b15aabb3Stbbdev } 5951c0b2f7Stbbdev //! Terminate interface 6051c0b2f7Stbbdev a.terminate(); 6151c0b2f7Stbbdev } 6251c0b2f7Stbbdev 6349e08aacStbbdev //! Test tasks isolation for inner oneapi::tbb::parallel_for loop 6451c0b2f7Stbbdev //! \brief \ref requirement \ref interface 6551c0b2f7Stbbdev TEST_CASE("Task isolation") { 6651c0b2f7Stbbdev const int N1 = 1000, N2 = 1000; 6749e08aacStbbdev oneapi::tbb::enumerable_thread_specific<int> ets; __anon3e0b77410302(int i) 6849e08aacStbbdev oneapi::tbb::parallel_for(0, N1, [&](int i) { 6951c0b2f7Stbbdev // Set a thread specific value 7051c0b2f7Stbbdev ets.local() = i; 7151c0b2f7Stbbdev // Run the second parallel loop in an isolated region to prevent the current thread 7251c0b2f7Stbbdev // from taking tasks related to the outer parallel loop. 7349e08aacStbbdev oneapi::tbb::this_task_arena::isolate([&]{ 7449e08aacStbbdev oneapi::tbb::parallel_for(0, N2, utils::DummyBody(10)); 7551c0b2f7Stbbdev }); 7651c0b2f7Stbbdev REQUIRE(ets.local() == i); 7751c0b2f7Stbbdev }); 7851c0b2f7Stbbdev } 7951c0b2f7Stbbdev 8049e08aacStbbdev class conformance_observer: public oneapi::tbb::task_scheduler_observer { 8151c0b2f7Stbbdev public: 8251c0b2f7Stbbdev std::atomic<bool> is_entry_called{false}; 8351c0b2f7Stbbdev std::atomic<bool> is_exit_called{false}; 8451c0b2f7Stbbdev conformance_observer(oneapi::tbb::task_arena & a)8549e08aacStbbdev conformance_observer( oneapi::tbb::task_arena &a ) : oneapi::tbb::task_scheduler_observer(a) { 8651c0b2f7Stbbdev observe(true); // activate the observer 8751c0b2f7Stbbdev } 8851c0b2f7Stbbdev ~conformance_observer()89*74207e5dSAnton Potapov ~conformance_observer() { 90*74207e5dSAnton Potapov observe(false); 91*74207e5dSAnton Potapov } 92*74207e5dSAnton Potapov on_scheduler_entry(bool)9351c0b2f7Stbbdev void on_scheduler_entry(bool) override { 9451c0b2f7Stbbdev is_entry_called.store(true, std::memory_order_relaxed); 9551c0b2f7Stbbdev } 9651c0b2f7Stbbdev on_scheduler_exit(bool)9751c0b2f7Stbbdev void on_scheduler_exit(bool) override { 9851c0b2f7Stbbdev is_exit_called.store(true, std::memory_order_relaxed); 9951c0b2f7Stbbdev } 10051c0b2f7Stbbdev is_callbacks_called()10151c0b2f7Stbbdev bool is_callbacks_called() { 10251c0b2f7Stbbdev return is_entry_called.load(std::memory_order_relaxed) 10351c0b2f7Stbbdev && is_exit_called.load(std::memory_order_relaxed); 10451c0b2f7Stbbdev } 10551c0b2f7Stbbdev }; 10651c0b2f7Stbbdev 10751c0b2f7Stbbdev //! Test task arena observer interfaces 10851c0b2f7Stbbdev //! \brief \ref requirement \ref interface 10951c0b2f7Stbbdev TEST_CASE("Task arena observer") { 11049e08aacStbbdev oneapi::tbb::task_arena a; a.initialize(); 11151c0b2f7Stbbdev conformance_observer observer(a); __anon3e0b77410502null11251c0b2f7Stbbdev a.execute([&] { 11349e08aacStbbdev oneapi::tbb::parallel_for(0, 100, utils::DummyBody(10), oneapi::tbb::simple_partitioner()); 11451c0b2f7Stbbdev }); 11551c0b2f7Stbbdev REQUIRE(observer.is_callbacks_called()); 11651c0b2f7Stbbdev } 11751c0b2f7Stbbdev 11851c0b2f7Stbbdev //! Test task arena copy constructor 11951c0b2f7Stbbdev //! \brief \ref interface \ref requirement 12051c0b2f7Stbbdev TEST_CASE("Task arena copy constructor") { 12149e08aacStbbdev oneapi::tbb::task_arena arena(1); 12249e08aacStbbdev oneapi::tbb::task_arena copy = arena; 12351c0b2f7Stbbdev 12451c0b2f7Stbbdev REQUIRE(arena.max_concurrency() == copy.max_concurrency()); 12551c0b2f7Stbbdev REQUIRE(arena.is_active() == copy.is_active()); 12651c0b2f7Stbbdev } 12774b7fc74SAnton Potapov 12874b7fc74SAnton Potapov 12974b7fc74SAnton Potapov //! Basic test for arena::enqueue with task handle 13074b7fc74SAnton Potapov //! \brief \ref interface \ref requirement 13174b7fc74SAnton Potapov TEST_CASE("enqueue task_handle") { 13274b7fc74SAnton Potapov oneapi::tbb::task_arena arena; 13374b7fc74SAnton Potapov oneapi::tbb::task_group tg; 13474b7fc74SAnton Potapov 13574b7fc74SAnton Potapov //This flag is intentionally made non-atomic for Thread Sanitizer 13674b7fc74SAnton Potapov //to raise a flag if implementation of task_group is incorrect 13774b7fc74SAnton Potapov bool run{false}; 13874b7fc74SAnton Potapov __anon3e0b77410602null13974b7fc74SAnton Potapov auto task_handle = tg.defer([&]{ run = true; }); 14074b7fc74SAnton Potapov 14174b7fc74SAnton Potapov arena.enqueue(std::move(task_handle)); 14274b7fc74SAnton Potapov tg.wait(); 14374b7fc74SAnton Potapov 14474b7fc74SAnton Potapov CHECK(run == true); 14574b7fc74SAnton Potapov } 14674b7fc74SAnton Potapov 14774b7fc74SAnton Potapov //! Basic test for this_task_arena::enqueue with task handle 14874b7fc74SAnton Potapov //! \brief \ref interface \ref requirement 14974b7fc74SAnton Potapov TEST_CASE("this_task_arena::enqueue task_handle") { 15074b7fc74SAnton Potapov oneapi::tbb::task_arena arena; 15174b7fc74SAnton Potapov oneapi::tbb::task_group tg; 15274b7fc74SAnton Potapov 15374b7fc74SAnton Potapov //This flag is intentionally made non-atomic for Thread Sanitizer 15474b7fc74SAnton Potapov //to raise a flag if implementation of task_group is incorrect 15574b7fc74SAnton Potapov bool run{false}; 15674b7fc74SAnton Potapov __anon3e0b77410702null15774b7fc74SAnton Potapov arena.execute([&]{ 15874b7fc74SAnton Potapov auto task_handle = tg.defer([&]{ run = true; }); 15974b7fc74SAnton Potapov 16074b7fc74SAnton Potapov oneapi::tbb::this_task_arena::enqueue(std::move(task_handle)); 16174b7fc74SAnton Potapov }); 16274b7fc74SAnton Potapov 16374b7fc74SAnton Potapov tg.wait(); 16474b7fc74SAnton Potapov 16574b7fc74SAnton Potapov CHECK(run == true); 16674b7fc74SAnton Potapov } 16774b7fc74SAnton Potapov 16874b7fc74SAnton Potapov //TODO: Add 16974b7fc74SAnton Potapov //! Basic test for this_task_arena::enqueue with functor 17074b7fc74SAnton Potapov 17174b7fc74SAnton Potapov //! Test case for the common use-case of prolonging task_group lifetime 17274b7fc74SAnton Potapov //! \brief \ref interface \ref requirement 17374b7fc74SAnton Potapov TEST_CASE("this_task_arena::enqueue prolonging task_group") { 17474b7fc74SAnton Potapov oneapi::tbb::task_arena arena; 17574b7fc74SAnton Potapov oneapi::tbb::task_group tg; 17674b7fc74SAnton Potapov 17774b7fc74SAnton Potapov //This flag is intentionally made non-atomic for Thread Sanitizer 17874b7fc74SAnton Potapov //to raise a flag if implementation of task_group is incorrect 17974b7fc74SAnton Potapov bool run{false}; 18074b7fc74SAnton Potapov 18174b7fc74SAnton Potapov //block the task_group to wait on it __anon3e0b77410902null18274b7fc74SAnton Potapov auto task_handle = tg.defer([]{}); 18374b7fc74SAnton Potapov __anon3e0b77410a02null18474b7fc74SAnton Potapov arena.execute([&]{ 18574b7fc74SAnton Potapov oneapi::tbb::this_task_arena::enqueue([&]{ 18674b7fc74SAnton Potapov run = true; 18774b7fc74SAnton Potapov //release the task_group 18874b7fc74SAnton Potapov task_handle = oneapi::tbb::task_handle{}; 18974b7fc74SAnton Potapov }); 19074b7fc74SAnton Potapov }); 19174b7fc74SAnton Potapov 19274b7fc74SAnton Potapov tg.wait(); 19374b7fc74SAnton Potapov 19474b7fc74SAnton Potapov CHECK(run == true); 19574b7fc74SAnton Potapov } 19674b7fc74SAnton Potapov 19774b7fc74SAnton Potapov #if TBB_USE_EXCEPTIONS 19874b7fc74SAnton Potapov //! Basic test for exceptions in task_arena::enqueue with task_handle 19974b7fc74SAnton Potapov //! \brief \ref interface \ref requirement 20074b7fc74SAnton Potapov TEST_CASE("task_arena::enqueue(task_handle) exception propagation"){ 20174b7fc74SAnton Potapov oneapi::tbb::task_group tg; 20274b7fc74SAnton Potapov oneapi::tbb::task_arena arena; 20374b7fc74SAnton Potapov __anon3e0b77410c02null20474b7fc74SAnton Potapov oneapi::tbb::task_handle h = tg.defer([&]{ 20574b7fc74SAnton Potapov volatile bool suppress_unreachable_code_warning = true; 20674b7fc74SAnton Potapov if (suppress_unreachable_code_warning) { 20774b7fc74SAnton Potapov throw std::runtime_error{ "" }; 20874b7fc74SAnton Potapov } 20974b7fc74SAnton Potapov }); 21074b7fc74SAnton Potapov 21174b7fc74SAnton Potapov arena.enqueue(std::move(h)); 21274b7fc74SAnton Potapov 21374b7fc74SAnton Potapov CHECK_THROWS_AS(tg.wait(), std::runtime_error); 21474b7fc74SAnton Potapov } 21574b7fc74SAnton Potapov 21674b7fc74SAnton Potapov //! Basic test for exceptions in this_task_arena::enqueue with task_handle 21774b7fc74SAnton Potapov //! \brief \ref interface \ref requirement 21874b7fc74SAnton Potapov TEST_CASE("this_task_arena::enqueue(task_handle) exception propagation"){ 21974b7fc74SAnton Potapov oneapi::tbb::task_group tg; 22074b7fc74SAnton Potapov __anon3e0b77410d02null22174b7fc74SAnton Potapov oneapi::tbb::task_handle h = tg.defer([&]{ 22274b7fc74SAnton Potapov volatile bool suppress_unreachable_code_warning = true; 22374b7fc74SAnton Potapov if (suppress_unreachable_code_warning) { 22474b7fc74SAnton Potapov throw std::runtime_error{ "" }; 22574b7fc74SAnton Potapov } 22674b7fc74SAnton Potapov }); 22774b7fc74SAnton Potapov 22874b7fc74SAnton Potapov oneapi::tbb::this_task_arena::enqueue(std::move(h)); 22974b7fc74SAnton Potapov 23074b7fc74SAnton Potapov CHECK_THROWS_AS(tg.wait(), std::runtime_error); 23174b7fc74SAnton Potapov } 23274b7fc74SAnton Potapov 23374b7fc74SAnton Potapov #endif // TBB_USE_EXCEPTIONS 234