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 #ifndef __TBB_task_group_H 18 #define __TBB_task_group_H 19 20 #include "detail/_config.h" 21 #include "detail/_namespace_injection.h" 22 #include "detail/_assert.h" 23 #include "detail/_utils.h" 24 #include "detail/_template_helpers.h" 25 #include "detail/_exception.h" 26 #include "detail/_task.h" 27 #include "detail/_small_object_pool.h" 28 #include "detail/_intrusive_list_node.h" 29 30 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 31 #include "detail/_task_handle.h" 32 #endif 33 34 #include "profiling.h" 35 36 #include <type_traits> 37 38 #if _MSC_VER && !defined(__INTEL_COMPILER) 39 // Suppress warning: structure was padded due to alignment specifier 40 #pragma warning(push) 41 #pragma warning(disable:4324) 42 #endif 43 44 namespace tbb { 45 namespace detail { 46 47 namespace d1 { 48 class delegate_base; 49 class task_arena_base; 50 class task_group_context; 51 class task_group_base; 52 } 53 54 namespace r1 { 55 // Forward declarations 56 class tbb_exception_ptr; 57 class market; 58 class thread_data; 59 class task_dispatcher; 60 template <bool> 61 class context_guard_helper; 62 struct task_arena_impl; 63 class context_list; 64 65 void __TBB_EXPORTED_FUNC execute(d1::task_arena_base&, d1::delegate_base&); 66 void __TBB_EXPORTED_FUNC isolate_within_arena(d1::delegate_base&, std::intptr_t); 67 68 void __TBB_EXPORTED_FUNC initialize(d1::task_group_context&); 69 void __TBB_EXPORTED_FUNC destroy(d1::task_group_context&); 70 void __TBB_EXPORTED_FUNC reset(d1::task_group_context&); 71 bool __TBB_EXPORTED_FUNC cancel_group_execution(d1::task_group_context&); 72 bool __TBB_EXPORTED_FUNC is_group_execution_cancelled(d1::task_group_context&); 73 void __TBB_EXPORTED_FUNC capture_fp_settings(d1::task_group_context&); 74 75 struct task_group_context_impl; 76 } 77 78 namespace d2 { 79 80 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 81 namespace { 82 template<typename F> 83 d1::task* task_ptr_or_nullptr(F&& f); 84 } 85 86 template<typename F> 87 class function_task : public task_handle_task { 88 //TODO: apply empty base optimization here 89 const F m_func; 90 91 private: 92 d1::task* execute(d1::execution_data& ed) override { 93 __TBB_ASSERT(ed.context == &this->ctx(), "The task group context should be used for all tasks"); 94 task* res = task_ptr_or_nullptr(m_func); 95 finalize(&ed); 96 return res; 97 } 98 d1::task* cancel(d1::execution_data& ed) override { 99 finalize(&ed); 100 return nullptr; 101 } 102 public: 103 template<typename FF> 104 function_task(FF&& f, d1::wait_context& wo, d1::task_group_context& ctx, d1::small_object_allocator& alloc) 105 : task_handle_task{wo, ctx, alloc}, 106 m_func(std::forward<FF>(f)) {} 107 }; 108 109 namespace { 110 template<typename F> 111 d1::task* task_ptr_or_nullptr_impl(std::false_type, F&& f){ 112 task_handle th = std::forward<F>(f)(); 113 return task_handle_accessor::release(th); 114 } 115 116 template<typename F> 117 d1::task* task_ptr_or_nullptr_impl(std::true_type, F&& f){ 118 std::forward<F>(f)(); 119 return nullptr; 120 } 121 122 template<typename F> 123 d1::task* task_ptr_or_nullptr(F&& f){ 124 using is_void_t = std::is_void< 125 decltype(std::forward<F>(f)()) 126 >; 127 128 return task_ptr_or_nullptr_impl(is_void_t{}, std::forward<F>(f)); 129 } 130 } 131 #else 132 namespace { 133 template<typename F> 134 d1::task* task_ptr_or_nullptr(F&& f){ 135 std::forward<F>(f)(); 136 return nullptr; 137 } 138 } // namespace 139 #endif // __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 140 } // namespace d2 141 142 namespace d1 { 143 144 // This structure is left here for backward compatibility check 145 struct context_list_node { 146 std::atomic<context_list_node*> prev{}; 147 std::atomic<context_list_node*> next{}; 148 }; 149 150 //! Used to form groups of tasks 151 /** @ingroup task_scheduling 152 The context services explicit cancellation requests from user code, and unhandled 153 exceptions intercepted during tasks execution. Intercepting an exception results 154 in generating internal cancellation requests (which is processed in exactly the 155 same way as external ones). 156 157 The context is associated with one or more root tasks and defines the cancellation 158 group that includes all the descendants of the corresponding root task(s). Association 159 is established when a context object is passed as an argument to the task::allocate_root() 160 method. See task_group_context::task_group_context for more details. 161 162 The context can be bound to another one, and other contexts can be bound to it, 163 forming a tree-like structure: parent -> this -> children. Arrows here designate 164 cancellation propagation direction. If a task in a cancellation group is cancelled 165 all the other tasks in this group and groups bound to it (as children) get cancelled too. 166 **/ 167 class task_group_context : no_copy { 168 public: 169 enum traits_type { 170 fp_settings = 1 << 1, 171 concurrent_wait = 1 << 2, 172 default_traits = 0 173 }; 174 enum kind_type { 175 isolated, 176 bound 177 }; 178 private: 179 //! Space for platform-specific FPU settings. 180 /** Must only be accessed inside TBB binaries, and never directly in user 181 code or inline methods. */ 182 std::uint64_t my_cpu_ctl_env; 183 184 //! Specifies whether cancellation was requested for this task group. 185 std::atomic<std::uint32_t> my_cancellation_requested; 186 187 //! Versioning for run-time checks and behavioral traits of the context. 188 enum class task_group_context_version : std::uint8_t { 189 gold_2021U1 = 0, // version of task_group_context released in oneTBB 2021.1 GOLD 190 proxy_support = 1 // backward compatible support for 'this' context to act as a proxy 191 }; 192 task_group_context_version my_version; 193 194 //! The context traits. 195 struct context_traits { 196 bool fp_settings : 1; 197 bool concurrent_wait : 1; 198 bool bound : 1; 199 bool proxy : 1; // true if 'this' acts as a proxy for user-specified context 200 bool reserved1 : 1; 201 bool reserved2 : 1; 202 bool reserved3 : 1; 203 bool reserved4 : 1; 204 } my_traits; 205 206 static_assert(sizeof(context_traits) == 1, "Traits shall fit into one byte."); 207 208 static constexpr std::uint8_t may_have_children = 1; 209 //! The context internal state (currently only may_have_children). 210 std::atomic<std::uint8_t> my_state; 211 212 enum class lifetime_state : std::uint8_t { 213 created, 214 locked, 215 isolated, 216 bound, 217 dead 218 }; 219 220 //! The synchronization machine state to manage lifetime. 221 std::atomic<lifetime_state> my_lifetime_state; 222 223 union { 224 //! Pointer to the context of the parent cancellation group. NULL for isolated contexts. 225 task_group_context* my_parent; 226 227 //! Pointer to the actual context 'this' context represents a proxy of. 228 task_group_context* my_actual_context; 229 }; 230 231 //! Thread data instance that registered this context in its list. 232 r1::context_list* my_context_list; 233 static_assert(sizeof(std::atomic<r1::thread_data*>) == sizeof(r1::context_list*), "To preserve backward compatibility these types should have the same size"); 234 235 //! Used to form the thread specific list of contexts without additional memory allocation. 236 /** A context is included into the list of the current thread when its binding to 237 its parent happens. Any context can be present in the list of one thread only. **/ 238 intrusive_list_node my_node; 239 static_assert(sizeof(intrusive_list_node) == sizeof(context_list_node), "To preserve backward compatibility these types should have the same size"); 240 241 //! Pointer to the container storing exception being propagated across this task group. 242 std::atomic<r1::tbb_exception_ptr*> my_exception; 243 static_assert(sizeof(std::atomic<r1::tbb_exception_ptr*>) == sizeof(r1::tbb_exception_ptr*), 244 "backward compatibility check"); 245 246 //! Used to set and maintain stack stitching point for Intel Performance Tools. 247 void* my_itt_caller; 248 249 //! Description of algorithm for scheduler based instrumentation. 250 string_resource_index my_name; 251 252 char padding[max_nfs_size 253 - sizeof(std::uint64_t) // my_cpu_ctl_env 254 - sizeof(std::atomic<std::uint32_t>) // my_cancellation_requested 255 - sizeof(std::uint8_t) // my_version 256 - sizeof(context_traits) // my_traits 257 - sizeof(std::atomic<std::uint8_t>) // my_state 258 - sizeof(std::atomic<lifetime_state>) // my_lifetime_state 259 - sizeof(task_group_context*) // my_parent 260 - sizeof(r1::context_list*) // my_context_list 261 - sizeof(intrusive_list_node) // my_node 262 - sizeof(std::atomic<r1::tbb_exception_ptr*>) // my_exception 263 - sizeof(void*) // my_itt_caller 264 - sizeof(string_resource_index) // my_name 265 ]; 266 267 task_group_context(context_traits t, string_resource_index name) 268 : my_version{task_group_context_version::proxy_support}, my_name{name} 269 { 270 my_traits = t; // GCC4.8 issues warning list initialization for bitset (missing-field-initializers) 271 r1::initialize(*this); 272 } 273 274 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 275 task_group_context(task_group_context* actual_context) 276 : my_version{task_group_context_version::proxy_support} 277 , my_actual_context{actual_context} 278 { 279 __TBB_ASSERT(my_actual_context, "Passed pointer value points to nothing."); 280 my_traits.proxy = true; 281 my_name = actual_context->my_name; 282 283 // no need to initialize 'this' context as it acts as a proxy for my_actual_context, which 284 // initialization is a user-side responsibility. 285 } 286 #endif 287 288 static context_traits make_traits(kind_type relation_with_parent, std::uintptr_t user_traits) { 289 context_traits ct; 290 ct.fp_settings = (user_traits & fp_settings) == fp_settings; 291 ct.concurrent_wait = (user_traits & concurrent_wait) == concurrent_wait; 292 ct.bound = relation_with_parent == bound; 293 ct.proxy = false; 294 ct.reserved1 = ct.reserved2 = ct.reserved3 = ct.reserved4 = false; 295 return ct; 296 } 297 298 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 299 bool is_proxy() const { 300 return my_version >= task_group_context_version::proxy_support && my_traits.proxy; 301 } 302 #endif 303 304 task_group_context& actual_context() noexcept { 305 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 306 if (is_proxy()) { 307 __TBB_ASSERT(my_actual_context, "Actual task_group_context is not set."); 308 return *my_actual_context; 309 } 310 #endif 311 return *this; 312 } 313 314 const task_group_context& actual_context() const noexcept { 315 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 316 if (is_proxy()) { 317 __TBB_ASSERT(my_actual_context, "Actual task_group_context is not set."); 318 return *my_actual_context; 319 } 320 #endif 321 return *this; 322 } 323 324 public: 325 //! Default & binding constructor. 326 /** By default a bound context is created. That is this context will be bound 327 (as child) to the context of the currently executing task . Cancellation 328 requests passed to the parent context are propagated to all the contexts 329 bound to it. Similarly priority change is propagated from the parent context 330 to its children. 331 332 If task_group_context::isolated is used as the argument, then the tasks associated 333 with this context will never be affected by events in any other context. 334 335 Creating isolated contexts involve much less overhead, but they have limited 336 utility. Normally when an exception occurs in an algorithm that has nested 337 ones running, it is desirably to have all the nested algorithms cancelled 338 as well. Such a behavior requires nested algorithms to use bound contexts. 339 340 There is one good place where using isolated algorithms is beneficial. It is 341 an external thread. That is if a particular algorithm is invoked directly from 342 the external thread (not from a TBB task), supplying it with explicitly 343 created isolated context will result in a faster algorithm startup. 344 345 VERSIONING NOTE: 346 Implementation(s) of task_group_context constructor(s) cannot be made 347 entirely out-of-line because the run-time version must be set by the user 348 code. This will become critically important for binary compatibility, if 349 we ever have to change the size of the context object. **/ 350 351 task_group_context(kind_type relation_with_parent = bound, 352 std::uintptr_t t = default_traits) 353 : task_group_context(make_traits(relation_with_parent, t), CUSTOM_CTX) {} 354 355 // Custom constructor for instrumentation of oneTBB algorithm 356 task_group_context(string_resource_index name ) 357 : task_group_context(make_traits(bound, default_traits), name) {} 358 359 // Do not introduce any logic on user side since it might break state propagation assumptions 360 ~task_group_context() { 361 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 362 // When 'this' serves as a proxy, the initialization does not happen - nor should the 363 // destruction. 364 if (!is_proxy()) 365 #endif 366 { 367 r1::destroy(*this); 368 } 369 } 370 371 //! Forcefully reinitializes the context after the task tree it was associated with is completed. 372 /** Because the method assumes that all the tasks that used to be associated with 373 this context have already finished, calling it while the context is still 374 in use somewhere in the task hierarchy leads to undefined behavior. 375 376 IMPORTANT: This method is not thread safe! 377 378 The method does not change the context's parent if it is set. **/ 379 void reset() { 380 r1::reset(actual_context()); 381 } 382 383 //! Initiates cancellation of all tasks in this cancellation group and its subordinate groups. 384 /** \return false if cancellation has already been requested, true otherwise. 385 386 Note that canceling never fails. When false is returned, it just means that 387 another thread (or this one) has already sent cancellation request to this 388 context or to one of its ancestors (if this context is bound). It is guaranteed 389 that when this method is concurrently called on the same not yet cancelled 390 context, true will be returned by one and only one invocation. **/ 391 bool cancel_group_execution() { 392 return r1::cancel_group_execution(actual_context()); 393 } 394 395 //! Returns true if the context received cancellation request. 396 bool is_group_execution_cancelled() { 397 return r1::is_group_execution_cancelled(actual_context()); 398 } 399 400 #if __TBB_FP_CONTEXT 401 //! Captures the current FPU control settings to the context. 402 /** Because the method assumes that all the tasks that used to be associated with 403 this context have already finished, calling it while the context is still 404 in use somewhere in the task hierarchy leads to undefined behavior. 405 406 IMPORTANT: This method is not thread safe! 407 408 The method does not change the FPU control settings of the context's parent. **/ 409 void capture_fp_settings() { 410 r1::capture_fp_settings(actual_context()); 411 } 412 #endif 413 414 //! Returns the user visible context trait 415 std::uintptr_t traits() const { 416 std::uintptr_t t{}; 417 const task_group_context& ctx = actual_context(); 418 t |= ctx.my_traits.fp_settings ? fp_settings : 0; 419 t |= ctx.my_traits.concurrent_wait ? concurrent_wait : 0; 420 return t; 421 } 422 private: 423 //// TODO: cleanup friends 424 friend class r1::market; 425 friend class r1::thread_data; 426 friend class r1::task_dispatcher; 427 template <bool> 428 friend class r1::context_guard_helper; 429 friend struct r1::task_arena_impl; 430 friend struct r1::task_group_context_impl; 431 friend class task_group_base; 432 }; // class task_group_context 433 434 static_assert(sizeof(task_group_context) == 128, "Wrong size of task_group_context"); 435 436 enum task_group_status { 437 not_complete, 438 complete, 439 canceled 440 }; 441 442 class task_group; 443 class structured_task_group; 444 #if TBB_PREVIEW_ISOLATED_TASK_GROUP 445 class isolated_task_group; 446 #endif 447 448 template<typename F> 449 class function_task : public task { 450 const F m_func; 451 wait_context& m_wait_ctx; 452 small_object_allocator m_allocator; 453 454 void finalize(const execution_data& ed) { 455 // Make a local reference not to access this after destruction. 456 wait_context& wo = m_wait_ctx; 457 // Copy allocator to the stack 458 auto allocator = m_allocator; 459 // Destroy user functor before release wait. 460 this->~function_task(); 461 wo.release(); 462 463 allocator.deallocate(this, ed); 464 } 465 task* execute(execution_data& ed) override { 466 task* res = d2::task_ptr_or_nullptr(m_func); 467 finalize(ed); 468 return res; 469 } 470 task* cancel(execution_data& ed) override { 471 finalize(ed); 472 return nullptr; 473 } 474 public: 475 function_task(const F& f, wait_context& wo, small_object_allocator& alloc) 476 : m_func(f) 477 , m_wait_ctx(wo) 478 , m_allocator(alloc) {} 479 480 function_task(F&& f, wait_context& wo, small_object_allocator& alloc) 481 : m_func(std::move(f)) 482 , m_wait_ctx(wo) 483 , m_allocator(alloc) {} 484 }; 485 486 template <typename F> 487 class function_stack_task : public task { 488 const F& m_func; 489 wait_context& m_wait_ctx; 490 491 void finalize() { 492 m_wait_ctx.release(); 493 } 494 task* execute(execution_data&) override { 495 task* res = d2::task_ptr_or_nullptr(m_func); 496 finalize(); 497 return res; 498 } 499 task* cancel(execution_data&) override { 500 finalize(); 501 return nullptr; 502 } 503 public: 504 function_stack_task(const F& f, wait_context& wo) : m_func(f), m_wait_ctx(wo) {} 505 }; 506 507 class task_group_base : no_copy { 508 protected: 509 wait_context m_wait_ctx; 510 task_group_context m_context; 511 512 template<typename F> 513 task_group_status internal_run_and_wait(const F& f) { 514 function_stack_task<F> t{ f, m_wait_ctx }; 515 m_wait_ctx.reserve(); 516 bool cancellation_status = false; 517 try_call([&] { 518 execute_and_wait(t, context(), m_wait_ctx, context()); 519 }).on_completion([&] { 520 // TODO: the reset method is not thread-safe. Ensure the correct behavior. 521 cancellation_status = context().is_group_execution_cancelled(); 522 context().reset(); 523 }); 524 return cancellation_status ? canceled : complete; 525 } 526 527 template<typename F> 528 task* prepare_task(F&& f) { 529 m_wait_ctx.reserve(); 530 small_object_allocator alloc{}; 531 return alloc.new_object<function_task<typename std::decay<F>::type>>(std::forward<F>(f), m_wait_ctx, alloc); 532 } 533 534 task_group_context& context() noexcept { 535 return m_context.actual_context(); 536 } 537 538 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 539 template<typename F> 540 d2::task_handle prepare_task_handle(F&& f) { 541 m_wait_ctx.reserve(); 542 small_object_allocator alloc{}; 543 using function_task_t = d2::function_task<typename std::decay<F>::type>; 544 d2::task_handle_task* function_task_p = alloc.new_object<function_task_t>(std::forward<F>(f), m_wait_ctx, context(), alloc); 545 546 return d2::task_handle_accessor::construct(function_task_p); 547 } 548 #endif 549 550 public: 551 task_group_base(uintptr_t traits = 0) 552 : m_wait_ctx(0) 553 , m_context(task_group_context::bound, task_group_context::default_traits | traits) 554 {} 555 556 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 557 task_group_base(task_group_context& ctx) 558 : m_wait_ctx(0) 559 , m_context(&ctx) 560 {} 561 #endif 562 563 ~task_group_base() noexcept(false) { 564 if (m_wait_ctx.continue_execution()) { 565 #if __TBB_CPP17_UNCAUGHT_EXCEPTIONS_PRESENT 566 bool stack_unwinding_in_progress = std::uncaught_exceptions() > 0; 567 #else 568 bool stack_unwinding_in_progress = std::uncaught_exception(); 569 #endif 570 // Always attempt to do proper cleanup to avoid inevitable memory corruption 571 // in case of missing wait (for the sake of better testability & debuggability) 572 if (!context().is_group_execution_cancelled()) 573 cancel(); 574 d1::wait(m_wait_ctx, context()); 575 if (!stack_unwinding_in_progress) 576 throw_exception(exception_id::missing_wait); 577 } 578 } 579 580 task_group_status wait() { 581 bool cancellation_status = false; 582 try_call([&] { 583 d1::wait(m_wait_ctx, context()); 584 }).on_completion([&] { 585 // TODO: the reset method is not thread-safe. Ensure the correct behavior. 586 cancellation_status = m_context.is_group_execution_cancelled(); 587 context().reset(); 588 }); 589 return cancellation_status ? canceled : complete; 590 } 591 592 void cancel() { 593 context().cancel_group_execution(); 594 } 595 }; // class task_group_base 596 597 class task_group : public task_group_base { 598 public: 599 task_group() : task_group_base(task_group_context::concurrent_wait) {} 600 601 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 602 task_group(task_group_context& ctx) : task_group_base(ctx) {} 603 #endif 604 605 template<typename F> 606 void run(F&& f) { 607 spawn(*prepare_task(std::forward<F>(f)), context()); 608 } 609 610 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 611 void run(d2::task_handle&& h) { 612 if (h == nullptr) { 613 throw_exception(exception_id::bad_task_handle); 614 } 615 616 using acs = d2::task_handle_accessor; 617 if (&acs::ctx_of(h) != &context()) { 618 throw_exception(exception_id::bad_task_handle_wrong_task_group); 619 } 620 621 spawn(*acs::release(h), context()); 622 } 623 624 template<typename F> 625 d2::task_handle defer(F&& f) { 626 return prepare_task_handle(std::forward<F>(f)); 627 628 } 629 #endif //__TBB_PREVIEW_TASK_GROUP_EXTENSIONS 630 631 template<typename F> 632 task_group_status run_and_wait(const F& f) { 633 return internal_run_and_wait(f); 634 } 635 }; // class task_group 636 637 #if TBB_PREVIEW_ISOLATED_TASK_GROUP 638 class spawn_delegate : public delegate_base { 639 task* task_to_spawn; 640 task_group_context& context; 641 bool operator()() const override { 642 spawn(*task_to_spawn, context); 643 return true; 644 } 645 public: 646 spawn_delegate(task* a_task, task_group_context& ctx) 647 : task_to_spawn(a_task), context(ctx) 648 {} 649 }; 650 651 class wait_delegate : public delegate_base { 652 bool operator()() const override { 653 status = tg.wait(); 654 return true; 655 } 656 protected: 657 task_group& tg; 658 task_group_status& status; 659 public: 660 wait_delegate(task_group& a_group, task_group_status& tgs) 661 : tg(a_group), status(tgs) {} 662 }; 663 664 template<typename F> 665 class run_wait_delegate : public wait_delegate { 666 F& func; 667 bool operator()() const override { 668 status = tg.run_and_wait(func); 669 return true; 670 } 671 public: 672 run_wait_delegate(task_group& a_group, F& a_func, task_group_status& tgs) 673 : wait_delegate(a_group, tgs), func(a_func) {} 674 }; 675 676 class isolated_task_group : public task_group { 677 intptr_t this_isolation() { 678 return reinterpret_cast<intptr_t>(this); 679 } 680 public: 681 isolated_task_group() : task_group() {} 682 683 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 684 isolated_task_group(task_group_context& ctx) : task_group(ctx) {} 685 #endif 686 687 template<typename F> 688 void run(F&& f) { 689 spawn_delegate sd(prepare_task(std::forward<F>(f)), context()); 690 r1::isolate_within_arena(sd, this_isolation()); 691 } 692 693 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 694 void run(d2::task_handle&& h) { 695 if (h == nullptr) { 696 throw_exception(exception_id::bad_task_handle); 697 } 698 699 using acs = d2::task_handle_accessor; 700 if (&acs::ctx_of(h) != &context()) { 701 throw_exception(exception_id::bad_task_handle_wrong_task_group); 702 } 703 704 spawn_delegate sd(acs::release(h), context()); 705 r1::isolate_within_arena(sd, this_isolation()); 706 } 707 #endif //__TBB_PREVIEW_TASK_GROUP_EXTENSIONS 708 709 template<typename F> 710 task_group_status run_and_wait( const F& f ) { 711 task_group_status result = not_complete; 712 run_wait_delegate<const F> rwd(*this, f, result); 713 r1::isolate_within_arena(rwd, this_isolation()); 714 __TBB_ASSERT(result != not_complete, "premature exit from wait?"); 715 return result; 716 } 717 718 task_group_status wait() { 719 task_group_status result = not_complete; 720 wait_delegate wd(*this, result); 721 r1::isolate_within_arena(wd, this_isolation()); 722 __TBB_ASSERT(result != not_complete, "premature exit from wait?"); 723 return result; 724 } 725 }; // class isolated_task_group 726 #endif // TBB_PREVIEW_ISOLATED_TASK_GROUP 727 728 inline bool is_current_task_group_canceling() { 729 task_group_context* ctx = current_context(); 730 return ctx ? ctx->is_group_execution_cancelled() : false; 731 } 732 733 } // namespace d1 734 } // namespace detail 735 736 inline namespace v1 { 737 using detail::d1::task_group_context; 738 using detail::d1::task_group; 739 #if TBB_PREVIEW_ISOLATED_TASK_GROUP 740 using detail::d1::isolated_task_group; 741 #endif 742 743 using detail::d1::task_group_status; 744 using detail::d1::not_complete; 745 using detail::d1::complete; 746 using detail::d1::canceled; 747 748 using detail::d1::is_current_task_group_canceling; 749 using detail::r1::missing_wait; 750 751 #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 752 using detail::d2::task_handle; 753 #endif 754 } 755 756 } // namespace tbb 757 758 #if _MSC_VER && !defined(__INTEL_COMPILER) 759 #pragma warning(pop) // 4324 warning 760 #endif 761 762 #endif // __TBB_task_group_H 763