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