149e08aacStbbdev /*
2*c4568449SPavel Kumbrasev Copyright (c) 2005-2023 Intel Corporation
349e08aacStbbdev
449e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License");
549e08aacStbbdev you may not use this file except in compliance with the License.
649e08aacStbbdev You may obtain a copy of the License at
749e08aacStbbdev
849e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0
949e08aacStbbdev
1049e08aacStbbdev Unless required by applicable law or agreed to in writing, software
1149e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS,
1249e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349e08aacStbbdev See the License for the specific language governing permissions and
1449e08aacStbbdev limitations under the License.
1549e08aacStbbdev */
1649e08aacStbbdev
1749e08aacStbbdev #ifndef __TBB_task_group_H
1849e08aacStbbdev #define __TBB_task_group_H
1949e08aacStbbdev
2049e08aacStbbdev #include "detail/_config.h"
2149e08aacStbbdev #include "detail/_namespace_injection.h"
22478de5b1Stbbdev #include "detail/_assert.h"
2349e08aacStbbdev #include "detail/_utils.h"
24478de5b1Stbbdev #include "detail/_template_helpers.h"
2549e08aacStbbdev #include "detail/_exception.h"
2649e08aacStbbdev #include "detail/_task.h"
2749e08aacStbbdev #include "detail/_small_object_pool.h"
2835147e00SIlya Isaev #include "detail/_intrusive_list_node.h"
29fde18567SAnton Potapov #include "detail/_task_handle.h"
30fde18567SAnton Potapov
3149e08aacStbbdev #include "profiling.h"
3249e08aacStbbdev
33478de5b1Stbbdev #include <type_traits>
3449e08aacStbbdev
3549e08aacStbbdev #if _MSC_VER && !defined(__INTEL_COMPILER)
3649e08aacStbbdev // Suppress warning: structure was padded due to alignment specifier
3749e08aacStbbdev #pragma warning(push)
3849e08aacStbbdev #pragma warning(disable:4324)
3949e08aacStbbdev #endif
4049e08aacStbbdev
4149e08aacStbbdev namespace tbb {
4249e08aacStbbdev namespace detail {
4349e08aacStbbdev
4449e08aacStbbdev namespace d1 {
4549e08aacStbbdev class delegate_base;
4649e08aacStbbdev class task_arena_base;
4749e08aacStbbdev class task_group_context;
48478de5b1Stbbdev class task_group_base;
4949e08aacStbbdev }
5049e08aacStbbdev
5149e08aacStbbdev namespace r1 {
5249e08aacStbbdev // Forward declarations
5349e08aacStbbdev class tbb_exception_ptr;
54*c4568449SPavel Kumbrasev class cancellation_disseminator;
5549e08aacStbbdev class thread_data;
5649e08aacStbbdev class task_dispatcher;
5749e08aacStbbdev template <bool>
5849e08aacStbbdev class context_guard_helper;
5949e08aacStbbdev struct task_arena_impl;
6035147e00SIlya Isaev class context_list;
6149e08aacStbbdev
628827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC execute(d1::task_arena_base&, d1::delegate_base&);
638827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC isolate_within_arena(d1::delegate_base&, std::intptr_t);
6449e08aacStbbdev
658827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC initialize(d1::task_group_context&);
668827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC destroy(d1::task_group_context&);
678827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC reset(d1::task_group_context&);
688827ea7dSLong Nguyen TBB_EXPORT bool __TBB_EXPORTED_FUNC cancel_group_execution(d1::task_group_context&);
698827ea7dSLong Nguyen TBB_EXPORT bool __TBB_EXPORTED_FUNC is_group_execution_cancelled(d1::task_group_context&);
708827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC capture_fp_settings(d1::task_group_context&);
7149e08aacStbbdev
7249e08aacStbbdev struct task_group_context_impl;
7349e08aacStbbdev }
7449e08aacStbbdev
75478de5b1Stbbdev namespace d2 {
76478de5b1Stbbdev
77478de5b1Stbbdev namespace {
78478de5b1Stbbdev template<typename F>
79478de5b1Stbbdev d1::task* task_ptr_or_nullptr(F&& f);
80478de5b1Stbbdev }
81478de5b1Stbbdev
82478de5b1Stbbdev template<typename F>
83478de5b1Stbbdev class function_task : public task_handle_task {
84478de5b1Stbbdev //TODO: apply empty base optimization here
85478de5b1Stbbdev const F m_func;
86478de5b1Stbbdev
87478de5b1Stbbdev private:
execute(d1::execution_data & ed)88478de5b1Stbbdev d1::task* execute(d1::execution_data& ed) override {
89fde18567SAnton Potapov __TBB_ASSERT(ed.context == &this->ctx(), "The task group context should be used for all tasks");
90478de5b1Stbbdev task* res = task_ptr_or_nullptr(m_func);
91478de5b1Stbbdev finalize(&ed);
92478de5b1Stbbdev return res;
93478de5b1Stbbdev }
cancel(d1::execution_data & ed)94478de5b1Stbbdev d1::task* cancel(d1::execution_data& ed) override {
95478de5b1Stbbdev finalize(&ed);
96478de5b1Stbbdev return nullptr;
97478de5b1Stbbdev }
98478de5b1Stbbdev public:
99478de5b1Stbbdev template<typename FF>
function_task(FF && f,d1::wait_context & wo,d1::task_group_context & ctx,d1::small_object_allocator & alloc)100478de5b1Stbbdev function_task(FF&& f, d1::wait_context& wo, d1::task_group_context& ctx, d1::small_object_allocator& alloc)
101478de5b1Stbbdev : task_handle_task{wo, ctx, alloc},
102478de5b1Stbbdev m_func(std::forward<FF>(f)) {}
103478de5b1Stbbdev };
104478de5b1Stbbdev
10574b7fc74SAnton Potapov #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS
106478de5b1Stbbdev namespace {
107478de5b1Stbbdev template<typename F>
task_ptr_or_nullptr_impl(std::false_type,F && f)108478de5b1Stbbdev d1::task* task_ptr_or_nullptr_impl(std::false_type, F&& f){
109478de5b1Stbbdev task_handle th = std::forward<F>(f)();
110fde18567SAnton Potapov return task_handle_accessor::release(th);
111478de5b1Stbbdev }
112478de5b1Stbbdev
113478de5b1Stbbdev template<typename F>
task_ptr_or_nullptr_impl(std::true_type,F && f)114478de5b1Stbbdev d1::task* task_ptr_or_nullptr_impl(std::true_type, F&& f){
115478de5b1Stbbdev std::forward<F>(f)();
116478de5b1Stbbdev return nullptr;
117478de5b1Stbbdev }
118478de5b1Stbbdev
119478de5b1Stbbdev template<typename F>
task_ptr_or_nullptr(F && f)120478de5b1Stbbdev d1::task* task_ptr_or_nullptr(F&& f){
121478de5b1Stbbdev using is_void_t = std::is_void<
122478de5b1Stbbdev decltype(std::forward<F>(f)())
123478de5b1Stbbdev >;
124478de5b1Stbbdev
125478de5b1Stbbdev return task_ptr_or_nullptr_impl(is_void_t{}, std::forward<F>(f));
126478de5b1Stbbdev }
127478de5b1Stbbdev }
128478de5b1Stbbdev #else
129478de5b1Stbbdev namespace {
130478de5b1Stbbdev template<typename F>
task_ptr_or_nullptr(F && f)131478de5b1Stbbdev d1::task* task_ptr_or_nullptr(F&& f){
132478de5b1Stbbdev std::forward<F>(f)();
133478de5b1Stbbdev return nullptr;
134478de5b1Stbbdev }
135478de5b1Stbbdev } // namespace
136478de5b1Stbbdev #endif // __TBB_PREVIEW_TASK_GROUP_EXTENSIONS
137478de5b1Stbbdev } // namespace d2
138478de5b1Stbbdev
13949e08aacStbbdev namespace d1 {
14049e08aacStbbdev
14135147e00SIlya Isaev // This structure is left here for backward compatibility check
14249e08aacStbbdev struct context_list_node {
14349e08aacStbbdev std::atomic<context_list_node*> prev{};
14449e08aacStbbdev std::atomic<context_list_node*> next{};
14549e08aacStbbdev };
14649e08aacStbbdev
14749e08aacStbbdev //! Used to form groups of tasks
14849e08aacStbbdev /** @ingroup task_scheduling
14949e08aacStbbdev The context services explicit cancellation requests from user code, and unhandled
15049e08aacStbbdev exceptions intercepted during tasks execution. Intercepting an exception results
15149e08aacStbbdev in generating internal cancellation requests (which is processed in exactly the
15249e08aacStbbdev same way as external ones).
15349e08aacStbbdev
15449e08aacStbbdev The context is associated with one or more root tasks and defines the cancellation
15549e08aacStbbdev group that includes all the descendants of the corresponding root task(s). Association
15649e08aacStbbdev is established when a context object is passed as an argument to the task::allocate_root()
15749e08aacStbbdev method. See task_group_context::task_group_context for more details.
15849e08aacStbbdev
15949e08aacStbbdev The context can be bound to another one, and other contexts can be bound to it,
16049e08aacStbbdev forming a tree-like structure: parent -> this -> children. Arrows here designate
16149e08aacStbbdev cancellation propagation direction. If a task in a cancellation group is cancelled
16249e08aacStbbdev all the other tasks in this group and groups bound to it (as children) get cancelled too.
16349e08aacStbbdev **/
16449e08aacStbbdev class task_group_context : no_copy {
16549e08aacStbbdev public:
16649e08aacStbbdev enum traits_type {
16749e08aacStbbdev fp_settings = 1 << 1,
16849e08aacStbbdev concurrent_wait = 1 << 2,
16949e08aacStbbdev default_traits = 0
17049e08aacStbbdev };
17149e08aacStbbdev enum kind_type {
17249e08aacStbbdev isolated,
17349e08aacStbbdev bound
17449e08aacStbbdev };
17549e08aacStbbdev private:
17649e08aacStbbdev //! Space for platform-specific FPU settings.
17749e08aacStbbdev /** Must only be accessed inside TBB binaries, and never directly in user
17849e08aacStbbdev code or inline methods. */
17949e08aacStbbdev std::uint64_t my_cpu_ctl_env;
18049e08aacStbbdev
18149e08aacStbbdev //! Specifies whether cancellation was requested for this task group.
18249e08aacStbbdev std::atomic<std::uint32_t> my_cancellation_requested;
18349e08aacStbbdev
184478de5b1Stbbdev //! Versioning for run-time checks and behavioral traits of the context.
185478de5b1Stbbdev enum class task_group_context_version : std::uint8_t {
18684efdd2aSAnton Potapov unused = 1 // ensure that new versions, if any, will not clash with previously used ones
187478de5b1Stbbdev };
188478de5b1Stbbdev task_group_context_version my_version;
18949e08aacStbbdev
19049e08aacStbbdev //! The context traits.
19149e08aacStbbdev struct context_traits {
19249e08aacStbbdev bool fp_settings : 1;
19349e08aacStbbdev bool concurrent_wait : 1;
19449e08aacStbbdev bool bound : 1;
195478de5b1Stbbdev bool reserved1 : 1;
196478de5b1Stbbdev bool reserved2 : 1;
197478de5b1Stbbdev bool reserved3 : 1;
198478de5b1Stbbdev bool reserved4 : 1;
19984efdd2aSAnton Potapov bool reserved5 : 1;
20049e08aacStbbdev } my_traits;
20149e08aacStbbdev
20249e08aacStbbdev static_assert(sizeof(context_traits) == 1, "Traits shall fit into one byte.");
20349e08aacStbbdev
20449e08aacStbbdev static constexpr std::uint8_t may_have_children = 1;
20549e08aacStbbdev //! The context internal state (currently only may_have_children).
20684efdd2aSAnton Potapov std::atomic<std::uint8_t> my_may_have_children;
20749e08aacStbbdev
20884efdd2aSAnton Potapov enum class state : std::uint8_t {
20949e08aacStbbdev created,
21049e08aacStbbdev locked,
21149e08aacStbbdev isolated,
21249e08aacStbbdev bound,
21384efdd2aSAnton Potapov dead,
21484efdd2aSAnton Potapov proxy = std::uint8_t(-1) //the context is not the real one, but proxy to other one
21549e08aacStbbdev };
21649e08aacStbbdev
21749e08aacStbbdev //! The synchronization machine state to manage lifetime.
21884efdd2aSAnton Potapov std::atomic<state> my_state;
21949e08aacStbbdev
220478de5b1Stbbdev union {
22157f524caSIlya Isaev //! Pointer to the context of the parent cancellation group. nullptr for isolated contexts.
22249e08aacStbbdev task_group_context* my_parent;
22349e08aacStbbdev
224478de5b1Stbbdev //! Pointer to the actual context 'this' context represents a proxy of.
225478de5b1Stbbdev task_group_context* my_actual_context;
226478de5b1Stbbdev };
227478de5b1Stbbdev
22849e08aacStbbdev //! Thread data instance that registered this context in its list.
22935147e00SIlya Isaev r1::context_list* my_context_list;
23035147e00SIlya Isaev static_assert(sizeof(std::atomic<r1::thread_data*>) == sizeof(r1::context_list*), "To preserve backward compatibility these types should have the same size");
23149e08aacStbbdev
23249e08aacStbbdev //! Used to form the thread specific list of contexts without additional memory allocation.
23349e08aacStbbdev /** A context is included into the list of the current thread when its binding to
23449e08aacStbbdev its parent happens. Any context can be present in the list of one thread only. **/
23535147e00SIlya Isaev intrusive_list_node my_node;
23635147e00SIlya Isaev static_assert(sizeof(intrusive_list_node) == sizeof(context_list_node), "To preserve backward compatibility these types should have the same size");
23749e08aacStbbdev
23849e08aacStbbdev //! Pointer to the container storing exception being propagated across this task group.
239a080baf9SAlex std::atomic<r1::tbb_exception_ptr*> my_exception;
240a080baf9SAlex static_assert(sizeof(std::atomic<r1::tbb_exception_ptr*>) == sizeof(r1::tbb_exception_ptr*),
241a080baf9SAlex "backward compatibility check");
24249e08aacStbbdev
24349e08aacStbbdev //! Used to set and maintain stack stitching point for Intel Performance Tools.
24449e08aacStbbdev void* my_itt_caller;
24549e08aacStbbdev
24649e08aacStbbdev //! Description of algorithm for scheduler based instrumentation.
24749e08aacStbbdev string_resource_index my_name;
24849e08aacStbbdev
24949e08aacStbbdev char padding[max_nfs_size
25049e08aacStbbdev - sizeof(std::uint64_t) // my_cpu_ctl_env
25149e08aacStbbdev - sizeof(std::atomic<std::uint32_t>) // my_cancellation_requested
25249e08aacStbbdev - sizeof(std::uint8_t) // my_version
25349e08aacStbbdev - sizeof(context_traits) // my_traits
25449e08aacStbbdev - sizeof(std::atomic<std::uint8_t>) // my_state
25584efdd2aSAnton Potapov - sizeof(std::atomic<state>) // my_state
25649e08aacStbbdev - sizeof(task_group_context*) // my_parent
25735147e00SIlya Isaev - sizeof(r1::context_list*) // my_context_list
25835147e00SIlya Isaev - sizeof(intrusive_list_node) // my_node
259a080baf9SAlex - sizeof(std::atomic<r1::tbb_exception_ptr*>) // my_exception
26049e08aacStbbdev - sizeof(void*) // my_itt_caller
26149e08aacStbbdev - sizeof(string_resource_index) // my_name
26249e08aacStbbdev ];
26349e08aacStbbdev
task_group_context(context_traits t,string_resource_index name)26449e08aacStbbdev task_group_context(context_traits t, string_resource_index name)
26584efdd2aSAnton Potapov : my_version{task_group_context_version::unused}, my_name{name}
266478de5b1Stbbdev {
26749e08aacStbbdev my_traits = t; // GCC4.8 issues warning list initialization for bitset (missing-field-initializers)
26849e08aacStbbdev r1::initialize(*this);
26949e08aacStbbdev }
27049e08aacStbbdev
task_group_context(task_group_context * actual_context)271478de5b1Stbbdev task_group_context(task_group_context* actual_context)
27284efdd2aSAnton Potapov : my_version{task_group_context_version::unused}
27384efdd2aSAnton Potapov , my_state{state::proxy}
274478de5b1Stbbdev , my_actual_context{actual_context}
275478de5b1Stbbdev {
276478de5b1Stbbdev __TBB_ASSERT(my_actual_context, "Passed pointer value points to nothing.");
277478de5b1Stbbdev my_name = actual_context->my_name;
278478de5b1Stbbdev
279478de5b1Stbbdev // no need to initialize 'this' context as it acts as a proxy for my_actual_context, which
280478de5b1Stbbdev // initialization is a user-side responsibility.
281478de5b1Stbbdev }
282478de5b1Stbbdev
make_traits(kind_type relation_with_parent,std::uintptr_t user_traits)28349e08aacStbbdev static context_traits make_traits(kind_type relation_with_parent, std::uintptr_t user_traits) {
28449e08aacStbbdev context_traits ct;
28549e08aacStbbdev ct.fp_settings = (user_traits & fp_settings) == fp_settings;
28649e08aacStbbdev ct.concurrent_wait = (user_traits & concurrent_wait) == concurrent_wait;
287478de5b1Stbbdev ct.bound = relation_with_parent == bound;
28884efdd2aSAnton Potapov ct.reserved1 = ct.reserved2 = ct.reserved3 = ct.reserved4 = ct.reserved5 = false;
28949e08aacStbbdev return ct;
29049e08aacStbbdev }
29149e08aacStbbdev
is_proxy()292478de5b1Stbbdev bool is_proxy() const {
29384efdd2aSAnton Potapov return my_state.load(std::memory_order_relaxed) == state::proxy;
294478de5b1Stbbdev }
295478de5b1Stbbdev
actual_context()296478de5b1Stbbdev task_group_context& actual_context() noexcept {
297478de5b1Stbbdev if (is_proxy()) {
298478de5b1Stbbdev __TBB_ASSERT(my_actual_context, "Actual task_group_context is not set.");
299478de5b1Stbbdev return *my_actual_context;
300478de5b1Stbbdev }
301478de5b1Stbbdev return *this;
302478de5b1Stbbdev }
303478de5b1Stbbdev
actual_context()304478de5b1Stbbdev const task_group_context& actual_context() const noexcept {
305478de5b1Stbbdev if (is_proxy()) {
306478de5b1Stbbdev __TBB_ASSERT(my_actual_context, "Actual task_group_context is not set.");
307478de5b1Stbbdev return *my_actual_context;
308478de5b1Stbbdev }
309478de5b1Stbbdev return *this;
310478de5b1Stbbdev }
311478de5b1Stbbdev
31249e08aacStbbdev public:
31349e08aacStbbdev //! Default & binding constructor.
31449e08aacStbbdev /** By default a bound context is created. That is this context will be bound
31549e08aacStbbdev (as child) to the context of the currently executing task . Cancellation
31649e08aacStbbdev requests passed to the parent context are propagated to all the contexts
31749e08aacStbbdev bound to it. Similarly priority change is propagated from the parent context
31849e08aacStbbdev to its children.
31949e08aacStbbdev
32049e08aacStbbdev If task_group_context::isolated is used as the argument, then the tasks associated
32149e08aacStbbdev with this context will never be affected by events in any other context.
32249e08aacStbbdev
32349e08aacStbbdev Creating isolated contexts involve much less overhead, but they have limited
32449e08aacStbbdev utility. Normally when an exception occurs in an algorithm that has nested
32549e08aacStbbdev ones running, it is desirably to have all the nested algorithms cancelled
32649e08aacStbbdev as well. Such a behavior requires nested algorithms to use bound contexts.
32749e08aacStbbdev
32849e08aacStbbdev There is one good place where using isolated algorithms is beneficial. It is
329b15aabb3Stbbdev an external thread. That is if a particular algorithm is invoked directly from
330b15aabb3Stbbdev the external thread (not from a TBB task), supplying it with explicitly
33149e08aacStbbdev created isolated context will result in a faster algorithm startup.
33249e08aacStbbdev
33349e08aacStbbdev VERSIONING NOTE:
33449e08aacStbbdev Implementation(s) of task_group_context constructor(s) cannot be made
33549e08aacStbbdev entirely out-of-line because the run-time version must be set by the user
33649e08aacStbbdev code. This will become critically important for binary compatibility, if
33749e08aacStbbdev we ever have to change the size of the context object. **/
33849e08aacStbbdev
33949e08aacStbbdev task_group_context(kind_type relation_with_parent = bound,
34049e08aacStbbdev std::uintptr_t t = default_traits)
task_group_context(make_traits (relation_with_parent,t),CUSTOM_CTX)34149e08aacStbbdev : task_group_context(make_traits(relation_with_parent, t), CUSTOM_CTX) {}
34249e08aacStbbdev
34349e08aacStbbdev // Custom constructor for instrumentation of oneTBB algorithm
task_group_context(string_resource_index name)34449e08aacStbbdev task_group_context(string_resource_index name )
34549e08aacStbbdev : task_group_context(make_traits(bound, default_traits), name) {}
34649e08aacStbbdev
34749e08aacStbbdev // Do not introduce any logic on user side since it might break state propagation assumptions
~task_group_context()34849e08aacStbbdev ~task_group_context() {
349478de5b1Stbbdev // When 'this' serves as a proxy, the initialization does not happen - nor should the
350478de5b1Stbbdev // destruction.
351478de5b1Stbbdev if (!is_proxy())
352478de5b1Stbbdev {
35349e08aacStbbdev r1::destroy(*this);
35449e08aacStbbdev }
355478de5b1Stbbdev }
35649e08aacStbbdev
35749e08aacStbbdev //! Forcefully reinitializes the context after the task tree it was associated with is completed.
35849e08aacStbbdev /** Because the method assumes that all the tasks that used to be associated with
35949e08aacStbbdev this context have already finished, calling it while the context is still
36049e08aacStbbdev in use somewhere in the task hierarchy leads to undefined behavior.
36149e08aacStbbdev
36249e08aacStbbdev IMPORTANT: This method is not thread safe!
36349e08aacStbbdev
36449e08aacStbbdev The method does not change the context's parent if it is set. **/
reset()36549e08aacStbbdev void reset() {
366478de5b1Stbbdev r1::reset(actual_context());
36749e08aacStbbdev }
36849e08aacStbbdev
36949e08aacStbbdev //! Initiates cancellation of all tasks in this cancellation group and its subordinate groups.
37049e08aacStbbdev /** \return false if cancellation has already been requested, true otherwise.
37149e08aacStbbdev
37249e08aacStbbdev Note that canceling never fails. When false is returned, it just means that
37349e08aacStbbdev another thread (or this one) has already sent cancellation request to this
37449e08aacStbbdev context or to one of its ancestors (if this context is bound). It is guaranteed
37549e08aacStbbdev that when this method is concurrently called on the same not yet cancelled
37649e08aacStbbdev context, true will be returned by one and only one invocation. **/
cancel_group_execution()37749e08aacStbbdev bool cancel_group_execution() {
378478de5b1Stbbdev return r1::cancel_group_execution(actual_context());
37949e08aacStbbdev }
38049e08aacStbbdev
38149e08aacStbbdev //! Returns true if the context received cancellation request.
is_group_execution_cancelled()38249e08aacStbbdev bool is_group_execution_cancelled() {
383478de5b1Stbbdev return r1::is_group_execution_cancelled(actual_context());
38449e08aacStbbdev }
38549e08aacStbbdev
38649e08aacStbbdev #if __TBB_FP_CONTEXT
38749e08aacStbbdev //! Captures the current FPU control settings to the context.
38849e08aacStbbdev /** Because the method assumes that all the tasks that used to be associated with
38949e08aacStbbdev this context have already finished, calling it while the context is still
39049e08aacStbbdev in use somewhere in the task hierarchy leads to undefined behavior.
39149e08aacStbbdev
39249e08aacStbbdev IMPORTANT: This method is not thread safe!
39349e08aacStbbdev
39449e08aacStbbdev The method does not change the FPU control settings of the context's parent. **/
capture_fp_settings()39549e08aacStbbdev void capture_fp_settings() {
396478de5b1Stbbdev r1::capture_fp_settings(actual_context());
39749e08aacStbbdev }
39849e08aacStbbdev #endif
39949e08aacStbbdev
40049e08aacStbbdev //! Returns the user visible context trait
traits()40149e08aacStbbdev std::uintptr_t traits() const {
40249e08aacStbbdev std::uintptr_t t{};
403478de5b1Stbbdev const task_group_context& ctx = actual_context();
404478de5b1Stbbdev t |= ctx.my_traits.fp_settings ? fp_settings : 0;
405478de5b1Stbbdev t |= ctx.my_traits.concurrent_wait ? concurrent_wait : 0;
40649e08aacStbbdev return t;
40749e08aacStbbdev }
40849e08aacStbbdev private:
40949e08aacStbbdev //// TODO: cleanup friends
410*c4568449SPavel Kumbrasev friend class r1::cancellation_disseminator;
41149e08aacStbbdev friend class r1::thread_data;
41249e08aacStbbdev friend class r1::task_dispatcher;
41349e08aacStbbdev template <bool>
41449e08aacStbbdev friend class r1::context_guard_helper;
41549e08aacStbbdev friend struct r1::task_arena_impl;
41649e08aacStbbdev friend struct r1::task_group_context_impl;
417478de5b1Stbbdev friend class task_group_base;
41849e08aacStbbdev }; // class task_group_context
41949e08aacStbbdev
42049e08aacStbbdev static_assert(sizeof(task_group_context) == 128, "Wrong size of task_group_context");
42149e08aacStbbdev
42249e08aacStbbdev enum task_group_status {
42349e08aacStbbdev not_complete,
42449e08aacStbbdev complete,
42549e08aacStbbdev canceled
42649e08aacStbbdev };
42749e08aacStbbdev
42849e08aacStbbdev class task_group;
42949e08aacStbbdev class structured_task_group;
43049e08aacStbbdev #if TBB_PREVIEW_ISOLATED_TASK_GROUP
43149e08aacStbbdev class isolated_task_group;
43249e08aacStbbdev #endif
43349e08aacStbbdev
43449e08aacStbbdev template<typename F>
43549e08aacStbbdev class function_task : public task {
43649e08aacStbbdev const F m_func;
43749e08aacStbbdev wait_context& m_wait_ctx;
43849e08aacStbbdev small_object_allocator m_allocator;
43949e08aacStbbdev
finalize(const execution_data & ed)44049e08aacStbbdev void finalize(const execution_data& ed) {
44149e08aacStbbdev // Make a local reference not to access this after destruction.
44249e08aacStbbdev wait_context& wo = m_wait_ctx;
44349e08aacStbbdev // Copy allocator to the stack
44449e08aacStbbdev auto allocator = m_allocator;
44549e08aacStbbdev // Destroy user functor before release wait.
44649e08aacStbbdev this->~function_task();
44749e08aacStbbdev wo.release();
44849e08aacStbbdev
44949e08aacStbbdev allocator.deallocate(this, ed);
45049e08aacStbbdev }
execute(execution_data & ed)45149e08aacStbbdev task* execute(execution_data& ed) override {
452478de5b1Stbbdev task* res = d2::task_ptr_or_nullptr(m_func);
45349e08aacStbbdev finalize(ed);
454478de5b1Stbbdev return res;
45549e08aacStbbdev }
cancel(execution_data & ed)45649e08aacStbbdev task* cancel(execution_data& ed) override {
45749e08aacStbbdev finalize(ed);
45849e08aacStbbdev return nullptr;
45949e08aacStbbdev }
46049e08aacStbbdev public:
function_task(const F & f,wait_context & wo,small_object_allocator & alloc)46149e08aacStbbdev function_task(const F& f, wait_context& wo, small_object_allocator& alloc)
46249e08aacStbbdev : m_func(f)
46349e08aacStbbdev , m_wait_ctx(wo)
46449e08aacStbbdev , m_allocator(alloc) {}
46549e08aacStbbdev
function_task(F && f,wait_context & wo,small_object_allocator & alloc)46649e08aacStbbdev function_task(F&& f, wait_context& wo, small_object_allocator& alloc)
467b15aabb3Stbbdev : m_func(std::move(f))
46849e08aacStbbdev , m_wait_ctx(wo)
46949e08aacStbbdev , m_allocator(alloc) {}
47049e08aacStbbdev };
47149e08aacStbbdev
47249e08aacStbbdev template <typename F>
47349e08aacStbbdev class function_stack_task : public task {
47449e08aacStbbdev const F& m_func;
47549e08aacStbbdev wait_context& m_wait_ctx;
47649e08aacStbbdev
finalize()47749e08aacStbbdev void finalize() {
47849e08aacStbbdev m_wait_ctx.release();
47949e08aacStbbdev }
execute(execution_data &)48049e08aacStbbdev task* execute(execution_data&) override {
481478de5b1Stbbdev task* res = d2::task_ptr_or_nullptr(m_func);
48249e08aacStbbdev finalize();
483478de5b1Stbbdev return res;
48449e08aacStbbdev }
cancel(execution_data &)48549e08aacStbbdev task* cancel(execution_data&) override {
48649e08aacStbbdev finalize();
48749e08aacStbbdev return nullptr;
48849e08aacStbbdev }
48949e08aacStbbdev public:
function_stack_task(const F & f,wait_context & wo)49049e08aacStbbdev function_stack_task(const F& f, wait_context& wo) : m_func(f), m_wait_ctx(wo) {}
49149e08aacStbbdev };
49249e08aacStbbdev
49349e08aacStbbdev class task_group_base : no_copy {
49449e08aacStbbdev protected:
49549e08aacStbbdev wait_context m_wait_ctx;
49649e08aacStbbdev task_group_context m_context;
49749e08aacStbbdev
49849e08aacStbbdev template<typename F>
internal_run_and_wait(const F & f)49949e08aacStbbdev task_group_status internal_run_and_wait(const F& f) {
50049e08aacStbbdev function_stack_task<F> t{ f, m_wait_ctx };
50149e08aacStbbdev m_wait_ctx.reserve();
50249e08aacStbbdev bool cancellation_status = false;
50349e08aacStbbdev try_call([&] {
504478de5b1Stbbdev execute_and_wait(t, context(), m_wait_ctx, context());
50549e08aacStbbdev }).on_completion([&] {
50649e08aacStbbdev // TODO: the reset method is not thread-safe. Ensure the correct behavior.
507478de5b1Stbbdev cancellation_status = context().is_group_execution_cancelled();
508478de5b1Stbbdev context().reset();
50949e08aacStbbdev });
51049e08aacStbbdev return cancellation_status ? canceled : complete;
51149e08aacStbbdev }
51249e08aacStbbdev
internal_run_and_wait(d2::task_handle && h)513dd8f8a78SAnton Potapov task_group_status internal_run_and_wait(d2::task_handle&& h) {
51474b7fc74SAnton Potapov __TBB_ASSERT(h != nullptr, "Attempt to schedule empty task_handle");
515dd8f8a78SAnton Potapov
516dd8f8a78SAnton Potapov using acs = d2::task_handle_accessor;
51774b7fc74SAnton Potapov __TBB_ASSERT(&acs::ctx_of(h) == &context(), "Attempt to schedule task_handle into different task_group");
518dd8f8a78SAnton Potapov
519dd8f8a78SAnton Potapov bool cancellation_status = false;
520dd8f8a78SAnton Potapov try_call([&] {
521dd8f8a78SAnton Potapov execute_and_wait(*acs::release(h), context(), m_wait_ctx, context());
522dd8f8a78SAnton Potapov }).on_completion([&] {
523dd8f8a78SAnton Potapov // TODO: the reset method is not thread-safe. Ensure the correct behavior.
524dd8f8a78SAnton Potapov cancellation_status = context().is_group_execution_cancelled();
525dd8f8a78SAnton Potapov context().reset();
526dd8f8a78SAnton Potapov });
527dd8f8a78SAnton Potapov return cancellation_status ? canceled : complete;
528dd8f8a78SAnton Potapov }
52974b7fc74SAnton Potapov
53049e08aacStbbdev template<typename F>
prepare_task(F && f)53149e08aacStbbdev task* prepare_task(F&& f) {
53249e08aacStbbdev m_wait_ctx.reserve();
53349e08aacStbbdev small_object_allocator alloc{};
53449e08aacStbbdev return alloc.new_object<function_task<typename std::decay<F>::type>>(std::forward<F>(f), m_wait_ctx, alloc);
53549e08aacStbbdev }
53649e08aacStbbdev
context()537478de5b1Stbbdev task_group_context& context() noexcept {
538478de5b1Stbbdev return m_context.actual_context();
539478de5b1Stbbdev }
540478de5b1Stbbdev
541478de5b1Stbbdev template<typename F>
prepare_task_handle(F && f)542478de5b1Stbbdev d2::task_handle prepare_task_handle(F&& f) {
543478de5b1Stbbdev m_wait_ctx.reserve();
544478de5b1Stbbdev small_object_allocator alloc{};
545478de5b1Stbbdev using function_task_t = d2::function_task<typename std::decay<F>::type>;
546478de5b1Stbbdev d2::task_handle_task* function_task_p = alloc.new_object<function_task_t>(std::forward<F>(f), m_wait_ctx, context(), alloc);
547478de5b1Stbbdev
548fde18567SAnton Potapov return d2::task_handle_accessor::construct(function_task_p);
549478de5b1Stbbdev }
550478de5b1Stbbdev
55149e08aacStbbdev public:
55249e08aacStbbdev task_group_base(uintptr_t traits = 0)
55349e08aacStbbdev : m_wait_ctx(0)
55449e08aacStbbdev , m_context(task_group_context::bound, task_group_context::default_traits | traits)
555478de5b1Stbbdev {}
556478de5b1Stbbdev
task_group_base(task_group_context & ctx)557478de5b1Stbbdev task_group_base(task_group_context& ctx)
558478de5b1Stbbdev : m_wait_ctx(0)
559478de5b1Stbbdev , m_context(&ctx)
560478de5b1Stbbdev {}
56149e08aacStbbdev
noexcept(false)56249e08aacStbbdev ~task_group_base() noexcept(false) {
56349e08aacStbbdev if (m_wait_ctx.continue_execution()) {
56449e08aacStbbdev #if __TBB_CPP17_UNCAUGHT_EXCEPTIONS_PRESENT
56549e08aacStbbdev bool stack_unwinding_in_progress = std::uncaught_exceptions() > 0;
56649e08aacStbbdev #else
56749e08aacStbbdev bool stack_unwinding_in_progress = std::uncaught_exception();
56849e08aacStbbdev #endif
56949e08aacStbbdev // Always attempt to do proper cleanup to avoid inevitable memory corruption
57049e08aacStbbdev // in case of missing wait (for the sake of better testability & debuggability)
571478de5b1Stbbdev if (!context().is_group_execution_cancelled())
57249e08aacStbbdev cancel();
573478de5b1Stbbdev d1::wait(m_wait_ctx, context());
57449e08aacStbbdev if (!stack_unwinding_in_progress)
57549e08aacStbbdev throw_exception(exception_id::missing_wait);
57649e08aacStbbdev }
57749e08aacStbbdev }
57849e08aacStbbdev
wait()57949e08aacStbbdev task_group_status wait() {
58049e08aacStbbdev bool cancellation_status = false;
58149e08aacStbbdev try_call([&] {
582478de5b1Stbbdev d1::wait(m_wait_ctx, context());
58349e08aacStbbdev }).on_completion([&] {
58449e08aacStbbdev // TODO: the reset method is not thread-safe. Ensure the correct behavior.
58549e08aacStbbdev cancellation_status = m_context.is_group_execution_cancelled();
586478de5b1Stbbdev context().reset();
58749e08aacStbbdev });
58849e08aacStbbdev return cancellation_status ? canceled : complete;
58949e08aacStbbdev }
59049e08aacStbbdev
cancel()59149e08aacStbbdev void cancel() {
592478de5b1Stbbdev context().cancel_group_execution();
59349e08aacStbbdev }
59449e08aacStbbdev }; // class task_group_base
59549e08aacStbbdev
59649e08aacStbbdev class task_group : public task_group_base {
59749e08aacStbbdev public:
task_group()59849e08aacStbbdev task_group() : task_group_base(task_group_context::concurrent_wait) {}
task_group(task_group_context & ctx)599478de5b1Stbbdev task_group(task_group_context& ctx) : task_group_base(ctx) {}
600478de5b1Stbbdev
60149e08aacStbbdev template<typename F>
run(F && f)60249e08aacStbbdev void run(F&& f) {
603478de5b1Stbbdev spawn(*prepare_task(std::forward<F>(f)), context());
60449e08aacStbbdev }
60549e08aacStbbdev
run(d2::task_handle && h)606478de5b1Stbbdev void run(d2::task_handle&& h) {
60774b7fc74SAnton Potapov __TBB_ASSERT(h != nullptr, "Attempt to schedule empty task_handle");
608478de5b1Stbbdev
609fde18567SAnton Potapov using acs = d2::task_handle_accessor;
61074b7fc74SAnton Potapov __TBB_ASSERT(&acs::ctx_of(h) == &context(), "Attempt to schedule task_handle into different task_group");
611478de5b1Stbbdev
612fde18567SAnton Potapov spawn(*acs::release(h), context());
613478de5b1Stbbdev }
614478de5b1Stbbdev
615478de5b1Stbbdev template<typename F>
defer(F && f)616478de5b1Stbbdev d2::task_handle defer(F&& f) {
617478de5b1Stbbdev return prepare_task_handle(std::forward<F>(f));
618478de5b1Stbbdev
619478de5b1Stbbdev }
620478de5b1Stbbdev
62149e08aacStbbdev template<typename F>
run_and_wait(const F & f)62249e08aacStbbdev task_group_status run_and_wait(const F& f) {
62349e08aacStbbdev return internal_run_and_wait(f);
62449e08aacStbbdev }
625dd8f8a78SAnton Potapov
run_and_wait(d2::task_handle && h)626dd8f8a78SAnton Potapov task_group_status run_and_wait(d2::task_handle&& h) {
627dd8f8a78SAnton Potapov return internal_run_and_wait(std::move(h));
628dd8f8a78SAnton Potapov }
62949e08aacStbbdev }; // class task_group
63049e08aacStbbdev
63149e08aacStbbdev #if TBB_PREVIEW_ISOLATED_TASK_GROUP
63249e08aacStbbdev class spawn_delegate : public delegate_base {
63349e08aacStbbdev task* task_to_spawn;
63449e08aacStbbdev task_group_context& context;
operator()63549e08aacStbbdev bool operator()() const override {
63649e08aacStbbdev spawn(*task_to_spawn, context);
63749e08aacStbbdev return true;
63849e08aacStbbdev }
63949e08aacStbbdev public:
spawn_delegate(task * a_task,task_group_context & ctx)64049e08aacStbbdev spawn_delegate(task* a_task, task_group_context& ctx)
64149e08aacStbbdev : task_to_spawn(a_task), context(ctx)
64249e08aacStbbdev {}
64349e08aacStbbdev };
64449e08aacStbbdev
64549e08aacStbbdev class wait_delegate : public delegate_base {
operator()64649e08aacStbbdev bool operator()() const override {
64749e08aacStbbdev status = tg.wait();
64849e08aacStbbdev return true;
64949e08aacStbbdev }
65049e08aacStbbdev protected:
65149e08aacStbbdev task_group& tg;
65249e08aacStbbdev task_group_status& status;
65349e08aacStbbdev public:
wait_delegate(task_group & a_group,task_group_status & tgs)65449e08aacStbbdev wait_delegate(task_group& a_group, task_group_status& tgs)
65549e08aacStbbdev : tg(a_group), status(tgs) {}
65649e08aacStbbdev };
65749e08aacStbbdev
65849e08aacStbbdev template<typename F>
65949e08aacStbbdev class run_wait_delegate : public wait_delegate {
66049e08aacStbbdev F& func;
operator()66149e08aacStbbdev bool operator()() const override {
66249e08aacStbbdev status = tg.run_and_wait(func);
66349e08aacStbbdev return true;
66449e08aacStbbdev }
66549e08aacStbbdev public:
run_wait_delegate(task_group & a_group,F & a_func,task_group_status & tgs)66649e08aacStbbdev run_wait_delegate(task_group& a_group, F& a_func, task_group_status& tgs)
66749e08aacStbbdev : wait_delegate(a_group, tgs), func(a_func) {}
66849e08aacStbbdev };
66949e08aacStbbdev
67049e08aacStbbdev class isolated_task_group : public task_group {
this_isolation()67149e08aacStbbdev intptr_t this_isolation() {
67249e08aacStbbdev return reinterpret_cast<intptr_t>(this);
67349e08aacStbbdev }
67449e08aacStbbdev public:
isolated_task_group()67549e08aacStbbdev isolated_task_group() : task_group() {}
67649e08aacStbbdev
isolated_task_group(task_group_context & ctx)677478de5b1Stbbdev isolated_task_group(task_group_context& ctx) : task_group(ctx) {}
678478de5b1Stbbdev
67949e08aacStbbdev template<typename F>
run(F && f)68049e08aacStbbdev void run(F&& f) {
681478de5b1Stbbdev spawn_delegate sd(prepare_task(std::forward<F>(f)), context());
68249e08aacStbbdev r1::isolate_within_arena(sd, this_isolation());
68349e08aacStbbdev }
68449e08aacStbbdev
run(d2::task_handle && h)685fde18567SAnton Potapov void run(d2::task_handle&& h) {
68674b7fc74SAnton Potapov __TBB_ASSERT(h != nullptr, "Attempt to schedule empty task_handle");
687478de5b1Stbbdev
688fde18567SAnton Potapov using acs = d2::task_handle_accessor;
68974b7fc74SAnton Potapov __TBB_ASSERT(&acs::ctx_of(h) == &context(), "Attempt to schedule task_handle into different task_group");
690478de5b1Stbbdev
691fde18567SAnton Potapov spawn_delegate sd(acs::release(h), context());
692478de5b1Stbbdev r1::isolate_within_arena(sd, this_isolation());
693478de5b1Stbbdev }
694478de5b1Stbbdev
69549e08aacStbbdev template<typename F>
run_and_wait(const F & f)69649e08aacStbbdev task_group_status run_and_wait( const F& f ) {
69749e08aacStbbdev task_group_status result = not_complete;
69849e08aacStbbdev run_wait_delegate<const F> rwd(*this, f, result);
69949e08aacStbbdev r1::isolate_within_arena(rwd, this_isolation());
70049e08aacStbbdev __TBB_ASSERT(result != not_complete, "premature exit from wait?");
70149e08aacStbbdev return result;
70249e08aacStbbdev }
70349e08aacStbbdev
wait()70449e08aacStbbdev task_group_status wait() {
70549e08aacStbbdev task_group_status result = not_complete;
70649e08aacStbbdev wait_delegate wd(*this, result);
70749e08aacStbbdev r1::isolate_within_arena(wd, this_isolation());
70849e08aacStbbdev __TBB_ASSERT(result != not_complete, "premature exit from wait?");
70949e08aacStbbdev return result;
71049e08aacStbbdev }
71149e08aacStbbdev }; // class isolated_task_group
71249e08aacStbbdev #endif // TBB_PREVIEW_ISOLATED_TASK_GROUP
71349e08aacStbbdev
is_current_task_group_canceling()71449e08aacStbbdev inline bool is_current_task_group_canceling() {
71549e08aacStbbdev task_group_context* ctx = current_context();
71649e08aacStbbdev return ctx ? ctx->is_group_execution_cancelled() : false;
71749e08aacStbbdev }
71849e08aacStbbdev
71949e08aacStbbdev } // namespace d1
72049e08aacStbbdev } // namespace detail
72149e08aacStbbdev
72249e08aacStbbdev inline namespace v1 {
72349e08aacStbbdev using detail::d1::task_group_context;
72449e08aacStbbdev using detail::d1::task_group;
72549e08aacStbbdev #if TBB_PREVIEW_ISOLATED_TASK_GROUP
72649e08aacStbbdev using detail::d1::isolated_task_group;
72749e08aacStbbdev #endif
72849e08aacStbbdev
72949e08aacStbbdev using detail::d1::task_group_status;
73049e08aacStbbdev using detail::d1::not_complete;
73149e08aacStbbdev using detail::d1::complete;
73249e08aacStbbdev using detail::d1::canceled;
73349e08aacStbbdev
73449e08aacStbbdev using detail::d1::is_current_task_group_canceling;
73549e08aacStbbdev using detail::r1::missing_wait;
736478de5b1Stbbdev
737478de5b1Stbbdev using detail::d2::task_handle;
73849e08aacStbbdev }
73949e08aacStbbdev
74049e08aacStbbdev } // namespace tbb
74149e08aacStbbdev
74249e08aacStbbdev #if _MSC_VER && !defined(__INTEL_COMPILER)
74349e08aacStbbdev #pragma warning(pop) // 4324 warning
74449e08aacStbbdev #endif
74549e08aacStbbdev
74649e08aacStbbdev #endif // __TBB_task_group_H
747