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 #include "common/test.h" 17 #include "common/concurrency_tracker.h" 18 #include "common/spin_barrier.h" 19 #include "common/utils.h" 20 #include "common/utils_concurrency_limit.h" 21 22 #include "oneapi/tbb/global_control.h" 23 #include "oneapi/tbb/parallel_for.h" 24 25 #include <limits.h> 26 #include <thread> 27 28 //! \file conformance_global_control.cpp 29 //! \brief Test for [sched.global_control] specification 30 31 const std::size_t MB = 1024*1024; 32 33 void TestStackSizeSimpleControl() { 34 oneapi::tbb::global_control s0(oneapi::tbb::global_control::thread_stack_size, 1*MB); 35 36 { 37 oneapi::tbb::global_control s1(oneapi::tbb::global_control::thread_stack_size, 8*MB); 38 39 CHECK(8*MB == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size)); 40 } 41 CHECK(1*MB == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size)); 42 } 43 44 struct StackSizeRun : utils::NoAssign { 45 46 int num_threads; 47 utils::SpinBarrier *barr1, *barr2; 48 49 StackSizeRun(int threads, utils::SpinBarrier *b1, utils::SpinBarrier *b2) : 50 num_threads(threads), barr1(b1), barr2(b2) {} 51 void operator()( int id ) const { 52 oneapi::tbb::global_control s1(oneapi::tbb::global_control::thread_stack_size, (1+id)*MB); 53 54 barr1->wait(); 55 56 REQUIRE(num_threads*MB == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size)); 57 barr2->wait(); 58 } 59 }; 60 61 void TestStackSizeThreadsControl() { 62 int threads = 4; 63 utils::SpinBarrier barr1(threads), barr2(threads); 64 utils::NativeParallelFor( threads, StackSizeRun(threads, &barr1, &barr2) ); 65 } 66 67 void RunWorkersLimited(size_t parallelism, bool wait) 68 { 69 oneapi::tbb::global_control s(oneapi::tbb::global_control::max_allowed_parallelism, parallelism); 70 // try both configuration with already sleeping workers and with not yet sleeping 71 if (wait) 72 utils::Sleep(10); 73 const std::size_t expected_threads = (utils::get_platform_max_threads()==1)? 1 : parallelism; 74 utils::ExactConcurrencyLevel::check(expected_threads); 75 } 76 77 void TestWorkersConstraints() 78 { 79 const size_t max_parallelism = 80 oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); 81 if (max_parallelism > 3) { 82 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, max_parallelism-1); 83 CHECK_MESSAGE(max_parallelism-1 == 84 oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism), 85 "Allowed parallelism must be decreasable."); 86 oneapi::tbb::global_control c1(oneapi::tbb::global_control::max_allowed_parallelism, max_parallelism-2); 87 CHECK_MESSAGE(max_parallelism-2 == 88 oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism), 89 "Allowed parallelism must be decreasable."); 90 } 91 const size_t limit_par = utils::min(max_parallelism, 4U); 92 // check that constrains are really met 93 for (int wait=0; wait<2; wait++) { 94 for (size_t num=2; num<limit_par; num++) 95 RunWorkersLimited(num, wait==1); 96 for (size_t num=limit_par; num>1; num--) 97 RunWorkersLimited(num, wait==1); 98 } 99 } 100 101 struct SetUseRun: utils::NoAssign { 102 utils::SpinBarrier &barr; 103 104 SetUseRun(utils::SpinBarrier& b) : barr(b) {} 105 void operator()( int id ) const { 106 if (id == 0) { 107 for (int i=0; i<10; i++) { 108 oneapi::tbb::parallel_for(0, 1000, utils::DummyBody(10), oneapi::tbb::simple_partitioner()); 109 barr.wait(); 110 } 111 } else { 112 for (int i=0; i<10; i++) { 113 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 8); 114 barr.wait(); 115 } 116 } 117 } 118 }; 119 120 void TestConcurrentSetUseConcurrency() 121 { 122 utils::SpinBarrier barr(2); 123 NativeParallelFor( 2, SetUseRun(barr) ); 124 } 125 126 // check number of workers after autoinitialization 127 void TestAutoInit() 128 { 129 const size_t max_parallelism = 130 oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); 131 const unsigned expected_threads = utils::get_platform_max_threads()==1? 132 1 : (unsigned)max_parallelism; 133 utils::ExactConcurrencyLevel::check(expected_threads); 134 CHECK_MESSAGE(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism) 135 == max_parallelism, "max_allowed_parallelism must not be changed after auto init"); 136 if (max_parallelism > 2) { 137 // after autoinit it's possible to decrease workers number 138 oneapi::tbb::global_control s(oneapi::tbb::global_control::max_allowed_parallelism, max_parallelism-1); 139 utils::ExactConcurrencyLevel::check(max_parallelism-1); 140 } 141 } 142 143 class TestMultipleControlsRun { 144 utils::SpinBarrier &barrier; 145 public: 146 TestMultipleControlsRun(utils::SpinBarrier& b) : barrier(b) {} 147 void operator()( int id ) const { 148 barrier.wait(); 149 if (id) { 150 { 151 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 1); 152 utils::ExactConcurrencyLevel::check(1); 153 barrier.wait(); 154 } 155 utils::ExactConcurrencyLevel::check(1); 156 barrier.wait(); 157 { 158 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 2); 159 utils::ExactConcurrencyLevel::check(1); 160 barrier.wait(); 161 utils::ExactConcurrencyLevel::check(2); 162 barrier.wait(); 163 } 164 } else { 165 { 166 utils::ExactConcurrencyLevel::check(1); 167 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism, 1); 168 barrier.wait(); 169 utils::ExactConcurrencyLevel::check(1); 170 barrier.wait(); 171 utils::ExactConcurrencyLevel::check(1); 172 barrier.wait(); 173 } 174 utils::ExactConcurrencyLevel::check(2); 175 barrier.wait(); 176 } 177 } 178 }; 179 180 // test that global controls from different thread with overlapping lifetime 181 // still keep parallelism under control 182 void TestMultipleControls() 183 { 184 utils::SpinBarrier barrier(2); 185 utils::NativeParallelFor( 2, TestMultipleControlsRun(barrier) ); 186 } 187 188 #if !(__TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)) 189 //! Testing setting stack size 190 //! \brief \ref interface \ref requirement 191 TEST_CASE("setting stack size") { 192 std::size_t default_ss = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size); 193 CHECK(default_ss > 0); 194 TestStackSizeSimpleControl(); 195 TestStackSizeThreadsControl(); 196 CHECK(default_ss == oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::thread_stack_size)); 197 } 198 #endif 199 200 //! Testing setting max number of threads 201 //! \brief \ref interface \ref requirement 202 TEST_CASE("setting max number of threads") { 203 TestWorkersConstraints(); 204 TestConcurrentSetUseConcurrency(); 205 TestAutoInit(); 206 } 207 208 //! Test terminate_on_exception default value 209 //! \brief \ref interface \ref requirement 210 TEST_CASE("terminate_on_exception: default") { 211 std::size_t default_toe = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception); 212 CHECK(default_toe == 0); 213 } 214 215 //! Test terminate_on_exception in a nested case 216 //! \brief \ref interface \ref requirement 217 TEST_CASE("terminate_on_exception: nested") { 218 oneapi::tbb::global_control* c0; 219 { 220 oneapi::tbb::global_control c1(oneapi::tbb::global_control::terminate_on_exception, 1); 221 CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 1); 222 { 223 oneapi::tbb::global_control c2(oneapi::tbb::global_control::terminate_on_exception, 0); 224 CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 1); 225 } 226 CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 1); 227 c0 = new oneapi::tbb::global_control(oneapi::tbb::global_control::terminate_on_exception, 0); 228 } 229 CHECK(oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::terminate_on_exception) == 0); 230 delete c0; 231 } 232 233 // The test cannot work correctly with statically linked runtime. 234 // TODO: investigate a failure in debug with MSVC 235 #if !_MSC_VER || (defined(_DLL) && !defined(_DEBUG)) 236 #include <csetjmp> 237 238 // Overall, the test case is not safe because the dtors might not be called during long jump. 239 // Therefore, it makes sense to run the test case after all other test cases. 240 //! Test terminate_on_exception behavior 241 //! \brief \ref interface \ref requirement 242 TEST_CASE("terminate_on_exception: enabled") { 243 oneapi::tbb::global_control c(oneapi::tbb::global_control::terminate_on_exception, 1); 244 static bool terminate_handler_called; 245 terminate_handler_called = false; 246 247 #if TBB_USE_EXCEPTIONS 248 try { 249 #endif 250 static std::jmp_buf buffer; 251 std::terminate_handler prev = std::set_terminate([] { 252 CHECK(!terminate_handler_called); 253 terminate_handler_called = true; 254 std::longjmp(buffer, 1); 255 }); 256 #if _MSC_VER 257 #pragma warning(push) 258 #pragma warning(disable:4611) // interaction between '_setjmp' and C++ object destruction is non - portable 259 #endif 260 SUBCASE("internal exception") { 261 if (setjmp(buffer) == 0) { 262 oneapi::tbb::parallel_for(0, 1, -1, [](int) {}); 263 FAIL("Unreachable code"); 264 } 265 } 266 #if TBB_USE_EXCEPTIONS 267 SUBCASE("user exception") { 268 if (setjmp(buffer) == 0) { 269 oneapi::tbb::parallel_for(0, 1, [](int) { 270 volatile bool suppress_unreachable_code_warning = true; 271 if (suppress_unreachable_code_warning) { 272 throw std::exception(); 273 } 274 }); 275 FAIL("Unreachable code"); 276 } 277 } 278 #endif 279 #if _MSC_VER 280 #pragma warning(pop) 281 #endif 282 std::set_terminate(prev); 283 terminate_handler_called = true; 284 #if TBB_USE_EXCEPTIONS 285 } catch (...) { 286 FAIL("The exception is not expected"); 287 } 288 #endif 289 CHECK(terminate_handler_called); 290 } 291 #endif 292 293 //! Testing setting the same value but different objects 294 //! \brief \ref interface \ref error_guessing 295 TEST_CASE("setting same value") { 296 const std::size_t value = 2; 297 298 oneapi::tbb::global_control* ctl1 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value); 299 oneapi::tbb::global_control* ctl2 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value); 300 301 std::size_t active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); 302 REQUIRE(active == value); 303 delete ctl2; 304 305 active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); 306 REQUIRE_MESSAGE(active == value, "Active value should not change, because of value duplication"); 307 delete ctl1; 308 } 309