1 /* 2 Copyright (c) 2005-2021 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 42 a.enqueue([&done] { 43 CHECK(oneapi::tbb::this_task_arena::max_concurrency() == 2); 44 done = true; 45 }); 46 //! Execute interface 47 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; 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 85 conformance_observer( oneapi::tbb::task_arena &a ) : oneapi::tbb::task_scheduler_observer(a) { 86 observe(true); // activate the observer 87 } 88 89 void on_scheduler_entry(bool) override { 90 is_entry_called.store(true, std::memory_order_relaxed); 91 } 92 93 void on_scheduler_exit(bool) override { 94 is_exit_called.store(true, std::memory_order_relaxed); 95 } 96 97 bool is_callbacks_called() { 98 return is_entry_called.load(std::memory_order_relaxed) 99 && is_exit_called.load(std::memory_order_relaxed); 100 } 101 }; 102 103 //! Test task arena observer interfaces 104 //! \brief \ref requirement \ref interface 105 TEST_CASE("Task arena observer") { 106 oneapi::tbb::task_arena a; a.initialize(); 107 conformance_observer observer(a); 108 a.execute([&] { 109 oneapi::tbb::parallel_for(0, 100, utils::DummyBody(10), oneapi::tbb::simple_partitioner()); 110 }); 111 REQUIRE(observer.is_callbacks_called()); 112 } 113 114 //! Test task arena copy constructor 115 //! \brief \ref interface \ref requirement 116 TEST_CASE("Task arena copy constructor") { 117 oneapi::tbb::task_arena arena(1); 118 oneapi::tbb::task_arena copy = arena; 119 120 REQUIRE(arena.max_concurrency() == copy.max_concurrency()); 121 REQUIRE(arena.is_active() == copy.is_active()); 122 } 123 124 125 //! Basic test for arena::enqueue with task handle 126 //! \brief \ref interface \ref requirement 127 TEST_CASE("enqueue task_handle") { 128 oneapi::tbb::task_arena arena; 129 oneapi::tbb::task_group tg; 130 131 //This flag is intentionally made non-atomic for Thread Sanitizer 132 //to raise a flag if implementation of task_group is incorrect 133 bool run{false}; 134 135 auto task_handle = tg.defer([&]{ run = true; }); 136 137 arena.enqueue(std::move(task_handle)); 138 tg.wait(); 139 140 CHECK(run == true); 141 } 142 143 //! Basic test for this_task_arena::enqueue with task handle 144 //! \brief \ref interface \ref requirement 145 TEST_CASE("this_task_arena::enqueue task_handle") { 146 oneapi::tbb::task_arena arena; 147 oneapi::tbb::task_group tg; 148 149 //This flag is intentionally made non-atomic for Thread Sanitizer 150 //to raise a flag if implementation of task_group is incorrect 151 bool run{false}; 152 153 arena.execute([&]{ 154 auto task_handle = tg.defer([&]{ run = true; }); 155 156 oneapi::tbb::this_task_arena::enqueue(std::move(task_handle)); 157 }); 158 159 tg.wait(); 160 161 CHECK(run == true); 162 } 163 164 //TODO: Add 165 //! Basic test for this_task_arena::enqueue with functor 166 167 //! Test case for the common use-case of prolonging task_group lifetime 168 //! \brief \ref interface \ref requirement 169 TEST_CASE("this_task_arena::enqueue prolonging task_group") { 170 oneapi::tbb::task_arena arena; 171 oneapi::tbb::task_group tg; 172 173 //This flag is intentionally made non-atomic for Thread Sanitizer 174 //to raise a flag if implementation of task_group is incorrect 175 bool run{false}; 176 177 //block the task_group to wait on it 178 auto task_handle = tg.defer([]{}); 179 180 arena.execute([&]{ 181 oneapi::tbb::this_task_arena::enqueue([&]{ 182 run = true; 183 //release the task_group 184 task_handle = oneapi::tbb::task_handle{}; 185 }); 186 }); 187 188 tg.wait(); 189 190 CHECK(run == true); 191 } 192 193 #if TBB_USE_EXCEPTIONS 194 //! Basic test for exceptions in task_arena::enqueue with task_handle 195 //! \brief \ref interface \ref requirement 196 TEST_CASE("task_arena::enqueue(task_handle) exception propagation"){ 197 oneapi::tbb::task_group tg; 198 oneapi::tbb::task_arena arena; 199 200 oneapi::tbb::task_handle h = tg.defer([&]{ 201 volatile bool suppress_unreachable_code_warning = true; 202 if (suppress_unreachable_code_warning) { 203 throw std::runtime_error{ "" }; 204 } 205 }); 206 207 arena.enqueue(std::move(h)); 208 209 CHECK_THROWS_AS(tg.wait(), std::runtime_error); 210 } 211 212 //! Basic test for exceptions in this_task_arena::enqueue with task_handle 213 //! \brief \ref interface \ref requirement 214 TEST_CASE("this_task_arena::enqueue(task_handle) exception propagation"){ 215 oneapi::tbb::task_group tg; 216 217 oneapi::tbb::task_handle h = tg.defer([&]{ 218 volatile bool suppress_unreachable_code_warning = true; 219 if (suppress_unreachable_code_warning) { 220 throw std::runtime_error{ "" }; 221 } 222 }); 223 224 oneapi::tbb::this_task_arena::enqueue(std::move(h)); 225 226 CHECK_THROWS_AS(tg.wait(), std::runtime_error); 227 } 228 229 #endif // TBB_USE_EXCEPTIONS 230