| 1 | /* |
| 2 | Copyright (c) 2005-2019 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 "harness_defs.h" |
| 18 | |
| 19 | //Concurrency scheduler is not supported by Windows* new UI apps |
| 20 | //TODO: check whether we can test anything here |
| 21 | #include "tbb/tbb_config.h" |
| 22 | #if !__TBB_WIN8UI_SUPPORT |
| 23 | #ifndef TBBTEST_USE_TBB |
| 24 | #define TBBTEST_USE_TBB 1 |
| 25 | #endif |
| 26 | #else |
| 27 | #define TBBTEST_USE_TBB 0 |
| 28 | #undef __TBB_TASK_GROUP_CONTEXT |
| 29 | #define __TBB_TASK_GROUP_CONTEXT 0 |
| 30 | #endif |
| 31 | |
| 32 | #if !TBBTEST_USE_TBB |
| 33 | #if defined(_MSC_VER) && _MSC_VER < 1600 |
| 34 | #ifdef TBBTEST_USE_TBB |
| 35 | #undef TBBTEST_USE_TBB |
| 36 | #endif |
| 37 | #define TBBTEST_USE_TBB 1 |
| 38 | #endif |
| 39 | #endif |
| 40 | |
| 41 | #if TBBTEST_USE_TBB |
| 42 | |
| 43 | #include "tbb/compat/ppl.h" |
| 44 | #include "tbb/task_scheduler_init.h" |
| 45 | |
| 46 | #if _MSC_VER |
| 47 | typedef tbb::internal::uint32_t uint_t; |
| 48 | #else |
| 49 | typedef uint32_t uint_t; |
| 50 | #endif |
| 51 | |
| 52 | #else /* !TBBTEST_USE_TBB */ |
| 53 | |
| 54 | #if defined(_MSC_VER) |
| 55 | #pragma warning(disable: 4100 4180) |
| 56 | #endif |
| 57 | |
| 58 | #include <ppl.h> |
| 59 | |
| 60 | typedef unsigned int uint_t; |
| 61 | |
| 62 | // Bug in this ConcRT version results in task_group::wait() rethrowing |
| 63 | // internal cancellation exception propagated by the scheduler from the nesting |
| 64 | // task group. |
| 65 | #define __TBB_SILENT_CANCELLATION_BROKEN (_MSC_VER == 1600) |
| 66 | |
| 67 | #endif /* !TBBTEST_USE_TBB */ |
| 68 | |
| 69 | #if __TBB_TASK_GROUP_CONTEXT |
| 70 | |
| 71 | #include "tbb/atomic.h" |
| 72 | #include "tbb/aligned_space.h" |
| 73 | #include "harness.h" |
| 74 | #include "harness_concurrency_tracker.h" |
| 75 | |
| 76 | unsigned g_MaxConcurrency = 0; |
| 77 | |
| 78 | typedef tbb::atomic<uint_t> atomic_t; |
| 79 | typedef Concurrency::task_handle<void(*)()> handle_type; |
| 80 | |
| 81 | //------------------------------------------------------------------------ |
| 82 | // Tests for the thread safety of the task_group manipulations |
| 83 | //------------------------------------------------------------------------ |
| 84 | |
| 85 | #include "harness_barrier.h" |
| 86 | |
| 87 | enum SharingMode { |
| 88 | VagabondGroup = 1, |
| 89 | ParallelWait = 2 |
| 90 | }; |
| 91 | |
| 92 | class SharedGroupBodyImpl : NoCopy, Harness::NoAfterlife { |
| 93 | static const uint_t c_numTasks0 = 4096, |
| 94 | c_numTasks1 = 1024; |
| 95 | |
| 96 | const uint_t m_numThreads; |
| 97 | const uint_t m_sharingMode; |
| 98 | |
| 99 | Concurrency::task_group *m_taskGroup; |
| 100 | atomic_t m_tasksSpawned, |
| 101 | m_threadsReady; |
| 102 | Harness::SpinBarrier m_barrier; |
| 103 | |
| 104 | static atomic_t s_tasksExecuted; |
| 105 | |
| 106 | struct TaskFunctor { |
| 107 | SharedGroupBodyImpl *m_pOwner; |
| 108 | void operator () () const { |
| 109 | if ( m_pOwner->m_sharingMode & ParallelWait ) { |
| 110 | while ( Harness::ConcurrencyTracker::PeakParallelism() < m_pOwner->m_numThreads ) |
| 111 | __TBB_Yield(); |
| 112 | } |
| 113 | ++s_tasksExecuted; |
| 114 | } |
| 115 | }; |
| 116 | |
| 117 | TaskFunctor m_taskFunctor; |
| 118 | |
| 119 | void Spawn ( uint_t numTasks ) { |
| 120 | for ( uint_t i = 0; i < numTasks; ++i ) { |
| 121 | ++m_tasksSpawned; |
| 122 | Harness::ConcurrencyTracker ct; |
| 123 | m_taskGroup->run( m_taskFunctor ); |
| 124 | } |
| 125 | ++m_threadsReady; |
| 126 | } |
| 127 | |
| 128 | void DeleteTaskGroup () { |
| 129 | delete m_taskGroup; |
| 130 | m_taskGroup = NULL; |
| 131 | } |
| 132 | |
| 133 | void Wait () { |
| 134 | while ( m_threadsReady != m_numThreads ) |
| 135 | __TBB_Yield(); |
| 136 | const uint_t numSpawned = c_numTasks0 + c_numTasks1 * (m_numThreads - 1); |
| 137 | ASSERT ( m_tasksSpawned == numSpawned, "Wrong number of spawned tasks. The test is broken" ); |
| 138 | REMARK("Max spawning parallelism is %u out of %u\n" , Harness::ConcurrencyTracker::PeakParallelism(), g_MaxConcurrency); |
| 139 | if ( m_sharingMode & ParallelWait ) { |
| 140 | m_barrier.wait( &Harness::ConcurrencyTracker::Reset ); |
| 141 | { |
| 142 | Harness::ConcurrencyTracker ct; |
| 143 | m_taskGroup->wait(); |
| 144 | } |
| 145 | if ( Harness::ConcurrencyTracker::PeakParallelism() == 1 ) |
| 146 | REPORT ( "Warning: No parallel waiting detected in TestParallelWait\n" ); |
| 147 | m_barrier.wait(); |
| 148 | } |
| 149 | else |
| 150 | m_taskGroup->wait(); |
| 151 | ASSERT ( m_tasksSpawned == numSpawned, "No tasks should be spawned after wait starts. The test is broken" ); |
| 152 | ASSERT ( s_tasksExecuted == numSpawned, "Not all spawned tasks were executed" ); |
| 153 | } |
| 154 | |
| 155 | public: |
| 156 | SharedGroupBodyImpl ( uint_t numThreads, uint_t sharingMode = 0 ) |
| 157 | : m_numThreads(numThreads) |
| 158 | , m_sharingMode(sharingMode) |
| 159 | , m_taskGroup(NULL) |
| 160 | , m_barrier(numThreads) |
| 161 | { |
| 162 | ASSERT ( m_numThreads > 1, "SharedGroupBody tests require concurrency" ); |
| 163 | ASSERT ( !(m_sharingMode & VagabondGroup) || m_numThreads == 2, "In vagabond mode SharedGroupBody must be used with 2 threads only" ); |
| 164 | Harness::ConcurrencyTracker::Reset(); |
| 165 | s_tasksExecuted = 0; |
| 166 | m_tasksSpawned = 0; |
| 167 | m_threadsReady = 0; |
| 168 | m_taskFunctor.m_pOwner = this; |
| 169 | } |
| 170 | |
| 171 | void Run ( uint_t idx ) { |
| 172 | #if TBBTEST_USE_TBB |
| 173 | tbb::task_scheduler_init init(g_MaxConcurrency); |
| 174 | #endif |
| 175 | AssertLive(); |
| 176 | if ( idx == 0 ) { |
| 177 | ASSERT ( !m_taskGroup && !m_tasksSpawned, "SharedGroupBody must be reset before reuse" ); |
| 178 | m_taskGroup = new Concurrency::task_group; |
| 179 | Spawn( c_numTasks0 ); |
| 180 | Wait(); |
| 181 | if ( m_sharingMode & VagabondGroup ) |
| 182 | m_barrier.wait(); |
| 183 | else |
| 184 | DeleteTaskGroup(); |
| 185 | } |
| 186 | else { |
| 187 | while ( m_tasksSpawned == 0 ) |
| 188 | __TBB_Yield(); |
| 189 | ASSERT ( m_taskGroup, "Task group is not initialized" ); |
| 190 | Spawn (c_numTasks1); |
| 191 | if ( m_sharingMode & ParallelWait ) |
| 192 | Wait(); |
| 193 | if ( m_sharingMode & VagabondGroup ) { |
| 194 | ASSERT ( idx == 1, "In vagabond mode SharedGroupBody must be used with 2 threads only" ); |
| 195 | m_barrier.wait(); |
| 196 | DeleteTaskGroup(); |
| 197 | } |
| 198 | } |
| 199 | AssertLive(); |
| 200 | } |
| 201 | }; |
| 202 | |
| 203 | atomic_t SharedGroupBodyImpl::s_tasksExecuted; |
| 204 | |
| 205 | class SharedGroupBody : NoAssign, Harness::NoAfterlife { |
| 206 | bool m_bOwner; |
| 207 | SharedGroupBodyImpl *m_pImpl; |
| 208 | public: |
| 209 | SharedGroupBody ( uint_t numThreads, uint_t sharingMode = 0 ) |
| 210 | : m_bOwner(true) |
| 211 | , m_pImpl( new SharedGroupBodyImpl(numThreads, sharingMode) ) |
| 212 | {} |
| 213 | SharedGroupBody ( const SharedGroupBody& src ) |
| 214 | : NoAssign() |
| 215 | , Harness::NoAfterlife() |
| 216 | , m_bOwner(false) |
| 217 | , m_pImpl(src.m_pImpl) |
| 218 | {} |
| 219 | ~SharedGroupBody () { |
| 220 | if ( m_bOwner ) |
| 221 | delete m_pImpl; |
| 222 | } |
| 223 | void operator() ( uint_t idx ) const { m_pImpl->Run(idx); } |
| 224 | }; |
| 225 | |
| 226 | class RunAndWaitSyncronizationTestBody : NoAssign { |
| 227 | Harness::SpinBarrier& m_barrier; |
| 228 | tbb::atomic<bool>& m_completed; |
| 229 | tbb::task_group& m_tg; |
| 230 | public: |
| 231 | RunAndWaitSyncronizationTestBody(Harness::SpinBarrier& barrier, tbb::atomic<bool>& completed, tbb::task_group& tg) |
| 232 | : m_barrier(barrier), m_completed(completed), m_tg(tg) {} |
| 233 | |
| 234 | void operator()() const { |
| 235 | m_barrier.wait(); |
| 236 | for (volatile int i = 0; i < 100000; ++i) {} |
| 237 | m_completed = true; |
| 238 | } |
| 239 | |
| 240 | void operator()(int id) const { |
| 241 | if (id == 0) { |
| 242 | m_tg.run_and_wait(*this); |
| 243 | } else { |
| 244 | m_barrier.wait(); |
| 245 | m_tg.wait(); |
| 246 | ASSERT(m_completed, "A concurrent waiter has left the wait method earlier than work has finished" ); |
| 247 | } |
| 248 | } |
| 249 | }; |
| 250 | |
| 251 | void TestParallelSpawn () { |
| 252 | NativeParallelFor( g_MaxConcurrency, SharedGroupBody(g_MaxConcurrency) ); |
| 253 | } |
| 254 | |
| 255 | void TestParallelWait () { |
| 256 | NativeParallelFor( g_MaxConcurrency, SharedGroupBody(g_MaxConcurrency, ParallelWait) ); |
| 257 | |
| 258 | Harness::SpinBarrier barrier(g_MaxConcurrency); |
| 259 | tbb::atomic<bool> completed; |
| 260 | completed = false; |
| 261 | tbb::task_group tg; |
| 262 | RunAndWaitSyncronizationTestBody b(barrier, completed, tg); |
| 263 | NativeParallelFor( g_MaxConcurrency, b ); |
| 264 | } |
| 265 | |
| 266 | // Tests non-stack-bound task group (the group that is allocated by one thread and destroyed by the other) |
| 267 | void TestVagabondGroup () { |
| 268 | NativeParallelFor( 2, SharedGroupBody(2, VagabondGroup) ); |
| 269 | } |
| 270 | |
| 271 | //------------------------------------------------------------------------ |
| 272 | // Common requisites of the Fibonacci tests |
| 273 | //------------------------------------------------------------------------ |
| 274 | |
| 275 | const uint_t N = 20; |
| 276 | const uint_t F = 6765; |
| 277 | |
| 278 | atomic_t g_Sum; |
| 279 | |
| 280 | #define FIB_TEST_PROLOGUE() \ |
| 281 | const unsigned numRepeats = g_MaxConcurrency * (TBB_USE_DEBUG ? 4 : 16); \ |
| 282 | Harness::ConcurrencyTracker::Reset() |
| 283 | |
| 284 | #define FIB_TEST_EPILOGUE(sum) \ |
| 285 | ASSERT( sum == numRepeats * F, NULL ); \ |
| 286 | REMARK("Realized parallelism in Fib test is %u out of %u\n", Harness::ConcurrencyTracker::PeakParallelism(), g_MaxConcurrency) |
| 287 | |
| 288 | //------------------------------------------------------------------------ |
| 289 | // Test for a complex tree of task groups |
| 290 | // |
| 291 | // The test executes a tree of task groups of the same sort with asymmetric |
| 292 | // descendant nodes distribution at each level at each level. |
| 293 | // |
| 294 | // The chores are specified as functor objects. Each task group contains only one chore. |
| 295 | //------------------------------------------------------------------------ |
| 296 | |
| 297 | template<uint_t Func(uint_t)> |
| 298 | struct FibTask : NoAssign, Harness::NoAfterlife { |
| 299 | uint_t* m_pRes; |
| 300 | const uint_t m_Num; |
| 301 | FibTask( uint_t* y, uint_t n ) : m_pRes(y), m_Num(n) {} |
| 302 | void operator() () const { |
| 303 | *m_pRes = Func(m_Num); |
| 304 | } |
| 305 | }; |
| 306 | |
| 307 | uint_t Fib_SpawnRightChildOnly ( uint_t n ) { |
| 308 | Harness::ConcurrencyTracker ct; |
| 309 | if( n<2 ) { |
| 310 | return n; |
| 311 | } else { |
| 312 | uint_t y = ~0u; |
| 313 | Concurrency::task_group tg; |
| 314 | tg.run( FibTask<Fib_SpawnRightChildOnly>(&y, n-1) ); |
| 315 | uint_t x = Fib_SpawnRightChildOnly(n-2); |
| 316 | tg.wait(); |
| 317 | return y+x; |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | void TestFib1 () { |
| 322 | FIB_TEST_PROLOGUE(); |
| 323 | uint_t sum = 0; |
| 324 | for( unsigned i = 0; i < numRepeats; ++i ) |
| 325 | sum += Fib_SpawnRightChildOnly(N); |
| 326 | FIB_TEST_EPILOGUE(sum); |
| 327 | } |
| 328 | |
| 329 | |
| 330 | //------------------------------------------------------------------------ |
| 331 | // Test for a mixed tree of task groups. |
| 332 | // |
| 333 | // The test executes a tree with multiple task of one sort at the first level, |
| 334 | // each of which originates in its turn a binary tree of descendant task groups. |
| 335 | // |
| 336 | // The chores are specified both as functor objects and as function pointers |
| 337 | //------------------------------------------------------------------------ |
| 338 | |
| 339 | uint_t Fib_SpawnBothChildren( uint_t n ) { |
| 340 | Harness::ConcurrencyTracker ct; |
| 341 | if( n<2 ) { |
| 342 | return n; |
| 343 | } else { |
| 344 | uint_t y = ~0u, |
| 345 | x = ~0u; |
| 346 | Concurrency::task_group tg; |
| 347 | tg.run( FibTask<Fib_SpawnBothChildren>(&x, n-2) ); |
| 348 | tg.run( FibTask<Fib_SpawnBothChildren>(&y, n-1) ); |
| 349 | tg.wait(); |
| 350 | return y + x; |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | void RunFib2 () { |
| 355 | g_Sum += Fib_SpawnBothChildren(N); |
| 356 | } |
| 357 | |
| 358 | void TestFib2 () { |
| 359 | FIB_TEST_PROLOGUE(); |
| 360 | g_Sum = 0; |
| 361 | Concurrency::task_group rg; |
| 362 | for( unsigned i = 0; i < numRepeats - 1; ++i ) |
| 363 | rg.run( &RunFib2 ); |
| 364 | rg.wait(); |
| 365 | rg.run( &RunFib2 ); |
| 366 | rg.wait(); |
| 367 | FIB_TEST_EPILOGUE(g_Sum); |
| 368 | } |
| 369 | |
| 370 | |
| 371 | //------------------------------------------------------------------------ |
| 372 | // Test for a complex tree of task groups |
| 373 | // The chores are specified as task handles for recursive functor objects. |
| 374 | //------------------------------------------------------------------------ |
| 375 | |
| 376 | class FibTask_SpawnRightChildOnly : NoAssign, Harness::NoAfterlife { |
| 377 | uint_t* m_pRes; |
| 378 | mutable uint_t m_Num; |
| 379 | |
| 380 | public: |
| 381 | FibTask_SpawnRightChildOnly( uint_t* y, uint_t n ) : m_pRes(y), m_Num(n) {} |
| 382 | void operator() () const { |
| 383 | Harness::ConcurrencyTracker ct; |
| 384 | AssertLive(); |
| 385 | if( m_Num < 2 ) { |
| 386 | *m_pRes = m_Num; |
| 387 | } else { |
| 388 | uint_t y = ~0u; |
| 389 | Concurrency::task_group tg; |
| 390 | Concurrency::task_handle<FibTask_SpawnRightChildOnly> h = FibTask_SpawnRightChildOnly(&y, m_Num-1); |
| 391 | tg.run( h ); |
| 392 | m_Num -= 2; |
| 393 | tg.run_and_wait( *this ); |
| 394 | *m_pRes += y; |
| 395 | } |
| 396 | } |
| 397 | }; |
| 398 | |
| 399 | uint_t RunFib3 ( uint_t n ) { |
| 400 | uint_t res = ~0u; |
| 401 | FibTask_SpawnRightChildOnly func(&res, n); |
| 402 | func(); |
| 403 | return res; |
| 404 | } |
| 405 | |
| 406 | void TestTaskHandle () { |
| 407 | FIB_TEST_PROLOGUE(); |
| 408 | uint_t sum = 0; |
| 409 | for( unsigned i = 0; i < numRepeats; ++i ) |
| 410 | sum += RunFib3(N); |
| 411 | FIB_TEST_EPILOGUE(sum); |
| 412 | } |
| 413 | |
| 414 | //------------------------------------------------------------------------ |
| 415 | // Test for a mixed tree of task groups. |
| 416 | // The chores are specified as task handles for both functor objects and function pointers |
| 417 | //------------------------------------------------------------------------ |
| 418 | |
| 419 | template<class task_group_type> |
| 420 | class FibTask_SpawnBothChildren : NoAssign, Harness::NoAfterlife { |
| 421 | uint_t* m_pRes; |
| 422 | uint_t m_Num; |
| 423 | public: |
| 424 | FibTask_SpawnBothChildren( uint_t* y, uint_t n ) : m_pRes(y), m_Num(n) {} |
| 425 | void operator() () const { |
| 426 | Harness::ConcurrencyTracker ct; |
| 427 | AssertLive(); |
| 428 | if( m_Num < 2 ) { |
| 429 | *m_pRes = m_Num; |
| 430 | } else { |
| 431 | uint_t x = ~0u, // initialized only to suppress warning |
| 432 | y = ~0u; |
| 433 | task_group_type tg; |
| 434 | Concurrency::task_handle<FibTask_SpawnBothChildren> h1 = FibTask_SpawnBothChildren(&y, m_Num-1), |
| 435 | h2 = FibTask_SpawnBothChildren(&x, m_Num-2); |
| 436 | tg.run( h1 ); |
| 437 | tg.run( h2 ); |
| 438 | tg.wait(); |
| 439 | *m_pRes = x + y; |
| 440 | } |
| 441 | } |
| 442 | }; |
| 443 | |
| 444 | template<class task_group_type> |
| 445 | void RunFib4 () { |
| 446 | uint_t res = ~0u; |
| 447 | FibTask_SpawnBothChildren<task_group_type> func(&res, N); |
| 448 | func(); |
| 449 | g_Sum += res; |
| 450 | } |
| 451 | |
| 452 | template<class task_group_type> |
| 453 | void TestTaskHandle2 () { |
| 454 | FIB_TEST_PROLOGUE(); |
| 455 | g_Sum = 0; |
| 456 | task_group_type rg; |
| 457 | typedef tbb::aligned_space<handle_type> handle_space_t; |
| 458 | handle_space_t *handles = new handle_space_t[numRepeats]; |
| 459 | handle_type *h = NULL; |
| 460 | #if __TBB_ipf && __TBB_GCC_VERSION==40601 |
| 461 | volatile // Workaround for unexpected exit from the loop below after the exception was caught |
| 462 | #endif |
| 463 | unsigned i = 0; |
| 464 | for( ;; ++i ) { |
| 465 | h = handles[i].begin(); |
| 466 | #if __TBB_FUNC_PTR_AS_TEMPL_PARAM_BROKEN |
| 467 | new ( h ) handle_type((void(*)())RunFib4<task_group_type>); |
| 468 | #else |
| 469 | new ( h ) handle_type(RunFib4<task_group_type>); |
| 470 | #endif |
| 471 | if ( i == numRepeats - 1 ) |
| 472 | break; |
| 473 | rg.run( *h ); |
| 474 | #if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
| 475 | bool caught = false; |
| 476 | try { |
| 477 | if( i&1 ) rg.run( *h ); |
| 478 | else rg.run_and_wait( *h ); |
| 479 | } |
| 480 | catch ( Concurrency::invalid_multiple_scheduling& e ) { |
| 481 | ASSERT( e.what(), "Error message is absent" ); |
| 482 | caught = true; |
| 483 | } |
| 484 | catch ( ... ) { |
| 485 | ASSERT ( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unrecognized exception" ); |
| 486 | } |
| 487 | ASSERT ( caught, "Expected invalid_multiple_scheduling exception is missing" ); |
| 488 | #endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */ |
| 489 | } |
| 490 | ASSERT( i == numRepeats - 1, "unexpected exit from the loop" ); |
| 491 | rg.run_and_wait( *h ); |
| 492 | |
| 493 | for( i = 0; i < numRepeats; ++i ) |
| 494 | #if __TBB_UNQUALIFIED_CALL_OF_DTOR_BROKEN |
| 495 | handles[i].begin()->Concurrency::task_handle<void(*)()>::~task_handle(); |
| 496 | #else |
| 497 | handles[i].begin()->~handle_type(); |
| 498 | #endif |
| 499 | delete []handles; |
| 500 | FIB_TEST_EPILOGUE(g_Sum); |
| 501 | } |
| 502 | |
| 503 | #if __TBB_CPP11_LAMBDAS_PRESENT |
| 504 | //------------------------------------------------------------------------ |
| 505 | // Test for a mixed tree of task groups. |
| 506 | // The chores are specified as lambdas |
| 507 | //------------------------------------------------------------------------ |
| 508 | |
| 509 | void TestFibWithLambdas () { |
| 510 | REMARK ("Lambdas test" ); |
| 511 | FIB_TEST_PROLOGUE(); |
| 512 | atomic_t sum; |
| 513 | sum = 0; |
| 514 | Concurrency::task_group rg; |
| 515 | for( unsigned i = 0; i < numRepeats; ++i ) |
| 516 | rg.run( [&](){sum += Fib_SpawnBothChildren(N);} ); |
| 517 | rg.wait(); |
| 518 | FIB_TEST_EPILOGUE(sum); |
| 519 | } |
| 520 | |
| 521 | //------------------------------------------------------------------------ |
| 522 | // Test for make_task. |
| 523 | // The chores are specified as lambdas converted to task_handles. |
| 524 | //------------------------------------------------------------------------ |
| 525 | |
| 526 | void TestFibWithMakeTask () { |
| 527 | REMARK ("Make_task test\n" ); |
| 528 | atomic_t sum; |
| 529 | sum = 0; |
| 530 | Concurrency::task_group rg; |
| 531 | const auto &h1 = Concurrency::make_task( [&](){sum += Fib_SpawnBothChildren(N);} ); |
| 532 | const auto &h2 = Concurrency::make_task( [&](){sum += Fib_SpawnBothChildren(N);} ); |
| 533 | rg.run( h1 ); |
| 534 | rg.run_and_wait( h2 ); |
| 535 | ASSERT( sum == 2 * F, NULL ); |
| 536 | } |
| 537 | #endif /* __TBB_CPP11_LAMBDAS_PRESENT */ |
| 538 | |
| 539 | |
| 540 | //------------------------------------------------------------------------ |
| 541 | // Tests for exception handling and cancellation behavior. |
| 542 | //------------------------------------------------------------------------ |
| 543 | |
| 544 | class test_exception : public std::exception |
| 545 | { |
| 546 | const char* m_strDescription; |
| 547 | public: |
| 548 | test_exception ( const char* descr ) : m_strDescription(descr) {} |
| 549 | |
| 550 | const char* what() const throw() __TBB_override { return m_strDescription; } |
| 551 | }; |
| 552 | |
| 553 | #if TBB_USE_CAPTURED_EXCEPTION |
| 554 | #include "tbb/tbb_exception.h" |
| 555 | typedef tbb::captured_exception TestException; |
| 556 | #else |
| 557 | typedef test_exception TestException; |
| 558 | #endif |
| 559 | |
| 560 | #include <string.h> |
| 561 | |
| 562 | #define NUM_CHORES 512 |
| 563 | #define NUM_GROUPS 64 |
| 564 | #define SKIP_CHORES (NUM_CHORES/4) |
| 565 | #define SKIP_GROUPS (NUM_GROUPS/4) |
| 566 | #define EXCEPTION_DESCR1 "Test exception 1" |
| 567 | #define EXCEPTION_DESCR2 "Test exception 2" |
| 568 | |
| 569 | atomic_t g_ExceptionCount; |
| 570 | atomic_t g_TaskCount; |
| 571 | unsigned g_ExecutedAtCancellation; |
| 572 | bool g_Rethrow; |
| 573 | bool g_Throw; |
| 574 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 575 | volatile bool g_CancellationPropagationInProgress; |
| 576 | #define CATCH_ANY() \ |
| 577 | __TBB_CATCH( ... ) { \ |
| 578 | if ( g_CancellationPropagationInProgress ) { \ |
| 579 | if ( g_Throw ) { \ |
| 580 | exceptionCaught = true; \ |
| 581 | ++g_ExceptionCount; \ |
| 582 | } \ |
| 583 | } else \ |
| 584 | ASSERT( false, "Unknown exception" ); \ |
| 585 | } |
| 586 | #else |
| 587 | #define CATCH_ANY() __TBB_CATCH( ... ) { ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unknown exception" ); } |
| 588 | #endif |
| 589 | |
| 590 | inline |
| 591 | void ResetGlobals ( bool bThrow, bool bRethrow ) { |
| 592 | g_Throw = bThrow; |
| 593 | g_Rethrow = bRethrow; |
| 594 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 595 | g_CancellationPropagationInProgress = false; |
| 596 | #endif |
| 597 | g_ExceptionCount = 0; |
| 598 | g_TaskCount = 0; |
| 599 | Harness::ConcurrencyTracker::Reset(); |
| 600 | } |
| 601 | |
| 602 | class ThrowingTask : NoAssign, Harness::NoAfterlife { |
| 603 | atomic_t &m_TaskCount; |
| 604 | public: |
| 605 | ThrowingTask( atomic_t& counter ) : m_TaskCount(counter) {} |
| 606 | void operator() () const { |
| 607 | Harness::ConcurrencyTracker ct; |
| 608 | AssertLive(); |
| 609 | if ( g_Throw ) { |
| 610 | if ( ++m_TaskCount == SKIP_CHORES ) |
| 611 | __TBB_THROW( test_exception(EXCEPTION_DESCR1) ); |
| 612 | __TBB_Yield(); |
| 613 | } |
| 614 | else { |
| 615 | ++g_TaskCount; |
| 616 | while( !Concurrency::is_current_task_group_canceling() ) |
| 617 | __TBB_Yield(); |
| 618 | } |
| 619 | } |
| 620 | }; |
| 621 | |
| 622 | void LaunchChildren () { |
| 623 | atomic_t count; |
| 624 | count = 0; |
| 625 | Concurrency::task_group g; |
| 626 | bool exceptionCaught = false; |
| 627 | for( unsigned i = 0; i < NUM_CHORES; ++i ) |
| 628 | g.run( ThrowingTask(count) ); |
| 629 | Concurrency::task_group_status status = Concurrency::not_complete; |
| 630 | __TBB_TRY { |
| 631 | status = g.wait(); |
| 632 | } __TBB_CATCH ( TestException& e ) { |
| 633 | #if TBB_USE_EXCEPTIONS |
| 634 | ASSERT( e.what(), "Empty what() string" ); |
| 635 | ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR1) == 0, "Unknown exception" ); |
| 636 | #endif /* TBB_USE_EXCEPTIONS */ |
| 637 | exceptionCaught = true; |
| 638 | ++g_ExceptionCount; |
| 639 | } CATCH_ANY(); |
| 640 | ASSERT( !g_Throw || exceptionCaught || status == Concurrency::canceled, "No exception in the child task group" ); |
| 641 | if ( g_Rethrow && g_ExceptionCount > SKIP_GROUPS ) { |
| 642 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 643 | g_CancellationPropagationInProgress = true; |
| 644 | #endif |
| 645 | __TBB_THROW( test_exception(EXCEPTION_DESCR2) ); |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | #if TBB_USE_EXCEPTIONS |
| 650 | void TestEh1 () { |
| 651 | ResetGlobals( true, false ); |
| 652 | Concurrency::task_group rg; |
| 653 | for( unsigned i = 0; i < NUM_GROUPS; ++i ) |
| 654 | // TBB version does not require taking function address |
| 655 | rg.run( &LaunchChildren ); |
| 656 | try { |
| 657 | rg.wait(); |
| 658 | } catch ( ... ) { |
| 659 | ASSERT( false, "Unexpected exception" ); |
| 660 | } |
| 661 | ASSERT( g_ExceptionCount <= NUM_GROUPS, "Too many exceptions from the child groups. The test is broken" ); |
| 662 | ASSERT( g_ExceptionCount == NUM_GROUPS, "Not all child groups threw the exception" ); |
| 663 | } |
| 664 | |
| 665 | void TestEh2 () { |
| 666 | ResetGlobals( true, true ); |
| 667 | Concurrency::task_group rg; |
| 668 | bool exceptionCaught = false; |
| 669 | for( unsigned i = 0; i < NUM_GROUPS; ++i ) |
| 670 | // TBB version does not require taking function address |
| 671 | rg.run( &LaunchChildren ); |
| 672 | try { |
| 673 | rg.wait(); |
| 674 | } catch ( TestException& e ) { |
| 675 | ASSERT( e.what(), "Empty what() string" ); |
| 676 | ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR2) == 0, "Unknown exception" ); |
| 677 | ASSERT ( !rg.is_canceling(), "wait() has not reset cancellation state" ); |
| 678 | exceptionCaught = true; |
| 679 | } CATCH_ANY(); |
| 680 | ASSERT( exceptionCaught, "No exception thrown from the root task group" ); |
| 681 | ASSERT( g_ExceptionCount >= SKIP_GROUPS, "Too few exceptions from the child groups. The test is broken" ); |
| 682 | ASSERT( g_ExceptionCount <= NUM_GROUPS - SKIP_GROUPS, "Too many exceptions from the child groups. The test is broken" ); |
| 683 | ASSERT( g_ExceptionCount < NUM_GROUPS - SKIP_GROUPS, "None of the child groups was cancelled" ); |
| 684 | } |
| 685 | #endif /* TBB_USE_EXCEPTIONS */ |
| 686 | |
| 687 | //------------------------------------------------------------------------ |
| 688 | // Tests for manual cancellation of the task_group hierarchy |
| 689 | //------------------------------------------------------------------------ |
| 690 | |
| 691 | void TestCancellation1 () { |
| 692 | ResetGlobals( false, false ); |
| 693 | Concurrency::task_group rg; |
| 694 | for( unsigned i = 0; i < NUM_GROUPS; ++i ) |
| 695 | // TBB version does not require taking function address |
| 696 | rg.run( &LaunchChildren ); |
| 697 | ASSERT ( !Concurrency::is_current_task_group_canceling(), "Unexpected cancellation" ); |
| 698 | ASSERT ( !rg.is_canceling(), "Unexpected cancellation" ); |
| 699 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 700 | g_CancellationPropagationInProgress = true; |
| 701 | #endif |
| 702 | while ( g_MaxConcurrency > 1 && g_TaskCount == 0 ) |
| 703 | __TBB_Yield(); |
| 704 | rg.cancel(); |
| 705 | g_ExecutedAtCancellation = g_TaskCount; |
| 706 | ASSERT ( rg.is_canceling(), "No cancellation reported" ); |
| 707 | rg.wait(); |
| 708 | ASSERT( g_TaskCount <= NUM_GROUPS * NUM_CHORES, "Too many tasks reported. The test is broken" ); |
| 709 | ASSERT( g_TaskCount < NUM_GROUPS * NUM_CHORES, "No tasks were cancelled. Cancellation model changed?" ); |
| 710 | ASSERT( g_TaskCount <= g_ExecutedAtCancellation + Harness::ConcurrencyTracker::PeakParallelism(), "Too many tasks survived cancellation" ); |
| 711 | } |
| 712 | |
| 713 | //------------------------------------------------------------------------ |
| 714 | // Tests for manual cancellation of the structured_task_group hierarchy |
| 715 | //------------------------------------------------------------------------ |
| 716 | |
| 717 | void StructuredLaunchChildren () { |
| 718 | atomic_t count; |
| 719 | count = 0; |
| 720 | Concurrency::structured_task_group g; |
| 721 | bool exceptionCaught = false; |
| 722 | typedef Concurrency::task_handle<ThrowingTask> throwing_handle_type; |
| 723 | tbb::aligned_space<throwing_handle_type,NUM_CHORES> handles; |
| 724 | for( unsigned i = 0; i < NUM_CHORES; ++i ) { |
| 725 | throwing_handle_type *h = handles.begin()+i; |
| 726 | new ( h ) throwing_handle_type( ThrowingTask(count) ); |
| 727 | g.run( *h ); |
| 728 | } |
| 729 | __TBB_TRY { |
| 730 | g.wait(); |
| 731 | } __TBB_CATCH( TestException& e ) { |
| 732 | #if TBB_USE_EXCEPTIONS |
| 733 | ASSERT( e.what(), "Empty what() string" ); |
| 734 | ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR1) == 0, "Unknown exception" ); |
| 735 | #endif /* TBB_USE_EXCEPTIONS */ |
| 736 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 737 | ASSERT ( !g.is_canceling() || g_CancellationPropagationInProgress, "wait() has not reset cancellation state" ); |
| 738 | #else |
| 739 | ASSERT ( !g.is_canceling(), "wait() has not reset cancellation state" ); |
| 740 | #endif |
| 741 | exceptionCaught = true; |
| 742 | ++g_ExceptionCount; |
| 743 | } CATCH_ANY(); |
| 744 | ASSERT( !g_Throw || exceptionCaught, "No exception in the child task group" ); |
| 745 | for( unsigned i = 0; i < NUM_CHORES; ++i ) |
| 746 | (handles.begin()+i)->~throwing_handle_type(); |
| 747 | if ( g_Rethrow && g_ExceptionCount > SKIP_GROUPS ) { |
| 748 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 749 | g_CancellationPropagationInProgress = true; |
| 750 | #endif |
| 751 | __TBB_THROW( test_exception(EXCEPTION_DESCR2) ); |
| 752 | } |
| 753 | } |
| 754 | |
| 755 | class StructuredCancellationTestDriver { |
| 756 | tbb::aligned_space<handle_type,NUM_CHORES> m_handles; |
| 757 | |
| 758 | public: |
| 759 | void Launch ( Concurrency::structured_task_group& rg ) { |
| 760 | ResetGlobals( false, false ); |
| 761 | for( unsigned i = 0; i < NUM_GROUPS; ++i ) { |
| 762 | handle_type *h = m_handles.begin()+i; |
| 763 | new ( h ) handle_type( StructuredLaunchChildren ); |
| 764 | rg.run( *h ); |
| 765 | } |
| 766 | ASSERT ( !Concurrency::is_current_task_group_canceling(), "Unexpected cancellation" ); |
| 767 | ASSERT ( !rg.is_canceling(), "Unexpected cancellation" ); |
| 768 | #if __TBB_SILENT_CANCELLATION_BROKEN |
| 769 | g_CancellationPropagationInProgress = true; |
| 770 | #endif |
| 771 | while ( g_MaxConcurrency > 1 && g_TaskCount == 0 ) |
| 772 | __TBB_Yield(); |
| 773 | } |
| 774 | |
| 775 | void Finish () { |
| 776 | for( unsigned i = 0; i < NUM_GROUPS; ++i ) |
| 777 | (m_handles.begin()+i)->~handle_type(); |
| 778 | ASSERT( g_TaskCount <= NUM_GROUPS * NUM_CHORES, "Too many tasks reported. The test is broken" ); |
| 779 | ASSERT( g_TaskCount < NUM_GROUPS * NUM_CHORES, "No tasks were cancelled. Cancellation model changed?" ); |
| 780 | ASSERT( g_TaskCount <= g_ExecutedAtCancellation + g_MaxConcurrency, "Too many tasks survived cancellation" ); |
| 781 | } |
| 782 | }; // StructuredCancellationTestDriver |
| 783 | |
| 784 | void TestStructuredCancellation1 () { |
| 785 | StructuredCancellationTestDriver driver; |
| 786 | Concurrency::structured_task_group sg; |
| 787 | driver.Launch( sg ); |
| 788 | sg.cancel(); |
| 789 | g_ExecutedAtCancellation = g_TaskCount; |
| 790 | ASSERT ( sg.is_canceling(), "No cancellation reported" ); |
| 791 | sg.wait(); |
| 792 | driver.Finish(); |
| 793 | } |
| 794 | |
| 795 | #if TBB_USE_EXCEPTIONS |
| 796 | #if defined(_MSC_VER) |
| 797 | #pragma warning (disable: 4127) |
| 798 | #endif |
| 799 | |
| 800 | template<bool Throw> |
| 801 | void TestStructuredCancellation2 () { |
| 802 | bool exception_occurred = false, |
| 803 | unexpected_exception = false; |
| 804 | StructuredCancellationTestDriver driver; |
| 805 | try { |
| 806 | Concurrency::structured_task_group tg; |
| 807 | driver.Launch( tg ); |
| 808 | if ( Throw ) |
| 809 | throw int(); // Initiate stack unwinding |
| 810 | } |
| 811 | catch ( const Concurrency::missing_wait& e ) { |
| 812 | ASSERT( e.what(), "Error message is absent" ); |
| 813 | exception_occurred = true; |
| 814 | unexpected_exception = Throw; |
| 815 | } |
| 816 | catch ( int ) { |
| 817 | exception_occurred = true; |
| 818 | unexpected_exception = !Throw; |
| 819 | } |
| 820 | catch ( ... ) { |
| 821 | exception_occurred = unexpected_exception = true; |
| 822 | } |
| 823 | ASSERT( exception_occurred, NULL ); |
| 824 | ASSERT( !unexpected_exception, NULL ); |
| 825 | driver.Finish(); |
| 826 | } |
| 827 | #endif /* TBB_USE_EXCEPTIONS */ |
| 828 | |
| 829 | void EmptyFunction () {} |
| 830 | |
| 831 | void TestStructuredWait () { |
| 832 | Concurrency::structured_task_group sg; |
| 833 | handle_type h(EmptyFunction); |
| 834 | sg.run(h); |
| 835 | sg.wait(); |
| 836 | handle_type h2(EmptyFunction); |
| 837 | sg.run(h2); |
| 838 | sg.wait(); |
| 839 | } |
| 840 | |
| 841 | struct TestFunctor { |
| 842 | void operator()() { ASSERT( false, "Non-const operator called" ); } |
| 843 | void operator()() const { /* library requires this overload only */ } |
| 844 | }; |
| 845 | |
| 846 | void TestConstantFunctorRequirement() { |
| 847 | tbb::task_group g; |
| 848 | TestFunctor tf; |
| 849 | g.run( tf ); g.wait(); |
| 850 | g.run_and_wait( tf ); |
| 851 | } |
| 852 | //------------------------------------------------------------------------ |
| 853 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
| 854 | namespace TestMoveSemanticsNS { |
| 855 | struct TestFunctor { |
| 856 | void operator()() const {}; |
| 857 | }; |
| 858 | |
| 859 | struct MoveOnlyFunctor : MoveOnly, TestFunctor { |
| 860 | MoveOnlyFunctor() : MoveOnly() {}; |
| 861 | MoveOnlyFunctor(MoveOnlyFunctor&& other) : MoveOnly(std::move(other)) {}; |
| 862 | }; |
| 863 | |
| 864 | struct MovePreferableFunctor : Movable, TestFunctor { |
| 865 | MovePreferableFunctor() : Movable() {}; |
| 866 | MovePreferableFunctor(MovePreferableFunctor&& other) : Movable(std::move(other)) {}; |
| 867 | MovePreferableFunctor(const MovePreferableFunctor& other) : Movable(other) {}; |
| 868 | }; |
| 869 | |
| 870 | struct NoMoveNoCopyFunctor : NoCopy, TestFunctor { |
| 871 | NoMoveNoCopyFunctor() : NoCopy() {}; |
| 872 | // mv ctor is not allowed as cp ctor from parent NoCopy |
| 873 | private: |
| 874 | NoMoveNoCopyFunctor(NoMoveNoCopyFunctor&&); |
| 875 | }; |
| 876 | |
| 877 | void TestFunctorsWithinTaskHandles() { |
| 878 | // working with task_handle rvalues is not supported in task_group |
| 879 | |
| 880 | tbb::task_group tg; |
| 881 | MovePreferableFunctor mpf; |
| 882 | typedef tbb::task_handle<MoveOnlyFunctor> th_mv_only_type; |
| 883 | typedef tbb::task_handle<MovePreferableFunctor> th_mv_pref_type; |
| 884 | |
| 885 | th_mv_only_type th_mv_only = th_mv_only_type(MoveOnlyFunctor()); |
| 886 | tg.run_and_wait(th_mv_only); |
| 887 | |
| 888 | th_mv_only_type th_mv_only1 = th_mv_only_type(MoveOnlyFunctor()); |
| 889 | tg.run(th_mv_only1); |
| 890 | tg.wait(); |
| 891 | |
| 892 | th_mv_pref_type th_mv_pref = th_mv_pref_type(mpf); |
| 893 | tg.run_and_wait(th_mv_pref); |
| 894 | ASSERT(mpf.alive, "object was moved when was passed by lval" ); |
| 895 | mpf.Reset(); |
| 896 | |
| 897 | th_mv_pref_type th_mv_pref1 = th_mv_pref_type(std::move(mpf)); |
| 898 | tg.run_and_wait(th_mv_pref1); |
| 899 | ASSERT(!mpf.alive, "object was copied when was passed by rval" ); |
| 900 | mpf.Reset(); |
| 901 | |
| 902 | th_mv_pref_type th_mv_pref2 = th_mv_pref_type(mpf); |
| 903 | tg.run(th_mv_pref2); |
| 904 | tg.wait(); |
| 905 | ASSERT(mpf.alive, "object was moved when was passed by lval" ); |
| 906 | mpf.Reset(); |
| 907 | |
| 908 | th_mv_pref_type th_mv_pref3 = th_mv_pref_type(std::move(mpf)); |
| 909 | tg.run(th_mv_pref3); |
| 910 | tg.wait(); |
| 911 | ASSERT(!mpf.alive, "object was copied when was passed by rval" ); |
| 912 | mpf.Reset(); |
| 913 | } |
| 914 | |
| 915 | void TestBareFunctors() { |
| 916 | tbb::task_group tg; |
| 917 | MovePreferableFunctor mpf; |
| 918 | // run_and_wait() doesn't have any copies or moves of arguments inside the impl |
| 919 | tg.run_and_wait( NoMoveNoCopyFunctor() ); |
| 920 | |
| 921 | tg.run( MoveOnlyFunctor() ); |
| 922 | tg.wait(); |
| 923 | |
| 924 | tg.run( mpf ); |
| 925 | tg.wait(); |
| 926 | ASSERT(mpf.alive, "object was moved when was passed by lval" ); |
| 927 | mpf.Reset(); |
| 928 | |
| 929 | tg.run( std::move(mpf) ); |
| 930 | tg.wait(); |
| 931 | ASSERT(!mpf.alive, "object was copied when was passed by rval" ); |
| 932 | mpf.Reset(); |
| 933 | } |
| 934 | |
| 935 | void TestMakeTask() { |
| 936 | MovePreferableFunctor mpf; |
| 937 | |
| 938 | tbb::make_task( MoveOnly() ); |
| 939 | |
| 940 | tbb::make_task( mpf ); |
| 941 | ASSERT(mpf.alive, "object was moved when was passed by lval" ); |
| 942 | mpf.Reset(); |
| 943 | |
| 944 | tbb::make_task( std::move(mpf) ); |
| 945 | ASSERT(!mpf.alive, "object was copied when was passed by rval" ); |
| 946 | mpf.Reset(); |
| 947 | } |
| 948 | } |
| 949 | #endif /* __TBB_CPP11_RVALUE_REF_PRESENT */ |
| 950 | |
| 951 | void TestMoveSemantics() { |
| 952 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
| 953 | TestMoveSemanticsNS::TestBareFunctors(); |
| 954 | TestMoveSemanticsNS::TestFunctorsWithinTaskHandles(); |
| 955 | TestMoveSemanticsNS::TestMakeTask(); |
| 956 | #else |
| 957 | REPORT("Known issue: move support tests are skipped.\n" ); |
| 958 | #endif |
| 959 | } |
| 960 | //------------------------------------------------------------------------ |
| 961 | |
| 962 | |
| 963 | int TestMain () { |
| 964 | REMARK ("Testing %s task_group functionality\n" , TBBTEST_USE_TBB ? "TBB" : "PPL" ); |
| 965 | for( int p=MinThread; p<=MaxThread; ++p ) { |
| 966 | g_MaxConcurrency = p; |
| 967 | #if TBBTEST_USE_TBB |
| 968 | tbb::task_scheduler_init init(p); |
| 969 | #else |
| 970 | Concurrency::SchedulerPolicy sp( 4, |
| 971 | Concurrency::SchedulerKind, Concurrency::ThreadScheduler, |
| 972 | Concurrency::MinConcurrency, 1, |
| 973 | Concurrency::MaxConcurrency, p, |
| 974 | Concurrency::TargetOversubscriptionFactor, 1); |
| 975 | Concurrency::Scheduler *s = Concurrency::Scheduler::Create( sp ); |
| 976 | #endif /* !TBBTEST_USE_TBB */ |
| 977 | if ( p > 1 ) { |
| 978 | TestParallelSpawn(); |
| 979 | TestParallelWait(); |
| 980 | TestVagabondGroup(); |
| 981 | } |
| 982 | TestFib1(); |
| 983 | TestFib2(); |
| 984 | TestTaskHandle(); |
| 985 | TestTaskHandle2<Concurrency::task_group>(); |
| 986 | TestTaskHandle2<Concurrency::structured_task_group>(); |
| 987 | #if __TBB_CPP11_LAMBDAS_PRESENT |
| 988 | TestFibWithLambdas(); |
| 989 | TestFibWithMakeTask(); |
| 990 | #endif |
| 991 | TestCancellation1(); |
| 992 | TestStructuredCancellation1(); |
| 993 | #if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
| 994 | TestEh1(); |
| 995 | TestEh2(); |
| 996 | TestStructuredWait(); |
| 997 | TestStructuredCancellation2<true>(); |
| 998 | #if !(__TBB_THROW_FROM_DTOR_BROKEN || __TBB_STD_UNCAUGHT_EXCEPTION_BROKEN) |
| 999 | TestStructuredCancellation2<false>(); |
| 1000 | #else |
| 1001 | REPORT("Known issue: TestStructuredCancellation2<false>() is skipped.\n" ); |
| 1002 | #endif |
| 1003 | #endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */ |
| 1004 | #if !TBBTEST_USE_TBB |
| 1005 | s->Release(); |
| 1006 | #endif |
| 1007 | } |
| 1008 | TestConstantFunctorRequirement(); |
| 1009 | #if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
| 1010 | REPORT("Known issue: exception handling tests are skipped.\n" ); |
| 1011 | #endif |
| 1012 | TestMoveSemantics(); |
| 1013 | return Harness::Done; |
| 1014 | } |
| 1015 | |
| 1016 | #else /* !__TBB_TASK_GROUP_CONTEXT */ |
| 1017 | |
| 1018 | #include "harness.h" |
| 1019 | |
| 1020 | int TestMain () { |
| 1021 | return Harness::Skipped; |
| 1022 | } |
| 1023 | |
| 1024 | #endif /* !__TBB_TASK_GROUP_CONTEXT */ |
| 1025 | |