1 /* 2 Copyright (c) 2005-2022 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #include "common/test.h" 18 #include "common/utils.h" 19 20 #include "oneapi/tbb/task_arena.h" 21 #include "oneapi/tbb/task_scheduler_observer.h" 22 #include "oneapi/tbb/enumerable_thread_specific.h" 23 #include "oneapi/tbb/parallel_for.h" 24 25 //! \file conformance_task_arena.cpp 26 //! \brief Test for [scheduler.task_arena scheduler.task_scheduler_observer] specification 27 28 // This test requires TBB in an uninitialized state 29 //! Test for uninitilized arena 30 //! \brief \ref requirement \ref interface 31 TEST_CASE("Test current_thread_index") { 32 REQUIRE_MESSAGE((tbb::this_task_arena::current_thread_index() == tbb::task_arena::not_initialized), "TBB was initialized state"); 33 } 34 35 //! Test task arena interfaces 36 //! \brief \ref requirement \ref interface 37 TEST_CASE("Arena interfaces") { 38 //! Initialization interfaces 39 oneapi::tbb::task_arena a(1,1); a.initialize(); 40 std::atomic<bool> done{ false }; 41 //! Enqueue interface __anon3e0b77410102null42 a.enqueue([&done] { 43 CHECK(oneapi::tbb::this_task_arena::max_concurrency() == 2); 44 done = true; 45 }); 46 //! Execute interface __anon3e0b77410202null47 a.execute([&] { 48 //! oneapi::tbb::this_task_arena interfaces 49 CHECK(oneapi::tbb::this_task_arena::current_thread_index() >= 0); 50 //! Attach interface 51 oneapi::tbb::task_arena attached_arena{oneapi::tbb::task_arena::attach()}; 52 CHECK(attached_arena.is_active()); 53 oneapi::tbb::task_arena attached_arena2{oneapi::tbb::attach()}; 54 CHECK(attached_arena2.is_active()); 55 }); 56 while (!done) { 57 utils::yield(); 58 } 59 //! Terminate interface 60 a.terminate(); 61 } 62 63 //! Test tasks isolation for inner oneapi::tbb::parallel_for loop 64 //! \brief \ref requirement \ref interface 65 TEST_CASE("Task isolation") { 66 const int N1 = 1000, N2 = 1000; 67 oneapi::tbb::enumerable_thread_specific<int> ets; __anon3e0b77410302(int i) 68 oneapi::tbb::parallel_for(0, N1, [&](int i) { 69 // Set a thread specific value 70 ets.local() = i; 71 // Run the second parallel loop in an isolated region to prevent the current thread 72 // from taking tasks related to the outer parallel loop. 73 oneapi::tbb::this_task_arena::isolate([&]{ 74 oneapi::tbb::parallel_for(0, N2, utils::DummyBody(10)); 75 }); 76 REQUIRE(ets.local() == i); 77 }); 78 } 79 80 class conformance_observer: public oneapi::tbb::task_scheduler_observer { 81 public: 82 std::atomic<bool> is_entry_called{false}; 83 std::atomic<bool> is_exit_called{false}; 84 conformance_observer(oneapi::tbb::task_arena & a)85 conformance_observer( oneapi::tbb::task_arena &a ) : oneapi::tbb::task_scheduler_observer(a) { 86 observe(true); // activate the observer 87 } 88 ~conformance_observer()89 ~conformance_observer() { 90 observe(false); 91 } 92 on_scheduler_entry(bool)93 void on_scheduler_entry(bool) override { 94 is_entry_called.store(true, std::memory_order_relaxed); 95 } 96 on_scheduler_exit(bool)97 void on_scheduler_exit(bool) override { 98 is_exit_called.store(true, std::memory_order_relaxed); 99 } 100 is_callbacks_called()101 bool is_callbacks_called() { 102 return is_entry_called.load(std::memory_order_relaxed) 103 && is_exit_called.load(std::memory_order_relaxed); 104 } 105 }; 106 107 //! Test task arena observer interfaces 108 //! \brief \ref requirement \ref interface 109 TEST_CASE("Task arena observer") { 110 oneapi::tbb::task_arena a; a.initialize(); 111 conformance_observer observer(a); __anon3e0b77410502null112 a.execute([&] { 113 oneapi::tbb::parallel_for(0, 100, utils::DummyBody(10), oneapi::tbb::simple_partitioner()); 114 }); 115 REQUIRE(observer.is_callbacks_called()); 116 } 117 118 //! Test task arena copy constructor 119 //! \brief \ref interface \ref requirement 120 TEST_CASE("Task arena copy constructor") { 121 oneapi::tbb::task_arena arena(1); 122 oneapi::tbb::task_arena copy = arena; 123 124 REQUIRE(arena.max_concurrency() == copy.max_concurrency()); 125 REQUIRE(arena.is_active() == copy.is_active()); 126 } 127 128 129 //! Basic test for arena::enqueue with task handle 130 //! \brief \ref interface \ref requirement 131 TEST_CASE("enqueue task_handle") { 132 oneapi::tbb::task_arena arena; 133 oneapi::tbb::task_group tg; 134 135 //This flag is intentionally made non-atomic for Thread Sanitizer 136 //to raise a flag if implementation of task_group is incorrect 137 bool run{false}; 138 __anon3e0b77410602null139 auto task_handle = tg.defer([&]{ run = true; }); 140 141 arena.enqueue(std::move(task_handle)); 142 tg.wait(); 143 144 CHECK(run == true); 145 } 146 147 //! Basic test for this_task_arena::enqueue with task handle 148 //! \brief \ref interface \ref requirement 149 TEST_CASE("this_task_arena::enqueue task_handle") { 150 oneapi::tbb::task_arena arena; 151 oneapi::tbb::task_group tg; 152 153 //This flag is intentionally made non-atomic for Thread Sanitizer 154 //to raise a flag if implementation of task_group is incorrect 155 bool run{false}; 156 __anon3e0b77410702null157 arena.execute([&]{ 158 auto task_handle = tg.defer([&]{ run = true; }); 159 160 oneapi::tbb::this_task_arena::enqueue(std::move(task_handle)); 161 }); 162 163 tg.wait(); 164 165 CHECK(run == true); 166 } 167 168 //TODO: Add 169 //! Basic test for this_task_arena::enqueue with functor 170 171 //! Test case for the common use-case of prolonging task_group lifetime 172 //! \brief \ref interface \ref requirement 173 TEST_CASE("this_task_arena::enqueue prolonging task_group") { 174 oneapi::tbb::task_arena arena; 175 oneapi::tbb::task_group tg; 176 177 //This flag is intentionally made non-atomic for Thread Sanitizer 178 //to raise a flag if implementation of task_group is incorrect 179 bool run{false}; 180 181 //block the task_group to wait on it __anon3e0b77410902null182 auto task_handle = tg.defer([]{}); 183 __anon3e0b77410a02null184 arena.execute([&]{ 185 oneapi::tbb::this_task_arena::enqueue([&]{ 186 run = true; 187 //release the task_group 188 task_handle = oneapi::tbb::task_handle{}; 189 }); 190 }); 191 192 tg.wait(); 193 194 CHECK(run == true); 195 } 196 197 #if TBB_USE_EXCEPTIONS 198 //! Basic test for exceptions in task_arena::enqueue with task_handle 199 //! \brief \ref interface \ref requirement 200 TEST_CASE("task_arena::enqueue(task_handle) exception propagation"){ 201 oneapi::tbb::task_group tg; 202 oneapi::tbb::task_arena arena; 203 __anon3e0b77410c02null204 oneapi::tbb::task_handle h = tg.defer([&]{ 205 volatile bool suppress_unreachable_code_warning = true; 206 if (suppress_unreachable_code_warning) { 207 throw std::runtime_error{ "" }; 208 } 209 }); 210 211 arena.enqueue(std::move(h)); 212 213 CHECK_THROWS_AS(tg.wait(), std::runtime_error); 214 } 215 216 //! Basic test for exceptions in this_task_arena::enqueue with task_handle 217 //! \brief \ref interface \ref requirement 218 TEST_CASE("this_task_arena::enqueue(task_handle) exception propagation"){ 219 oneapi::tbb::task_group tg; 220 __anon3e0b77410d02null221 oneapi::tbb::task_handle h = tg.defer([&]{ 222 volatile bool suppress_unreachable_code_warning = true; 223 if (suppress_unreachable_code_warning) { 224 throw std::runtime_error{ "" }; 225 } 226 }); 227 228 oneapi::tbb::this_task_arena::enqueue(std::move(h)); 229 230 CHECK_THROWS_AS(tg.wait(), std::runtime_error); 231 } 232 233 #endif // TBB_USE_EXCEPTIONS 234