Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-18 10:24:20

0001 /*
0002     Copyright (c) 2020-2024 Intel Corporation
0003 
0004     Licensed under the Apache License, Version 2.0 (the "License");
0005     you may not use this file except in compliance with the License.
0006     You may obtain a copy of the License at
0007 
0008         http://www.apache.org/licenses/LICENSE-2.0
0009 
0010     Unless required by applicable law or agreed to in writing, software
0011     distributed under the License is distributed on an "AS IS" BASIS,
0012     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013     See the License for the specific language governing permissions and
0014     limitations under the License.
0015 */
0016 
0017 #ifndef __TBB__task_H
0018 #define __TBB__task_H
0019 
0020 #include "_config.h"
0021 #include "_assert.h"
0022 #include "_template_helpers.h"
0023 #include "_small_object_pool.h"
0024 
0025 #include "../profiling.h"
0026 
0027 #include <cstddef>
0028 #include <cstdint>
0029 #include <climits>
0030 #include <utility>
0031 #include <atomic>
0032 #include <mutex>
0033 
0034 namespace tbb {
0035 namespace detail {
0036 
0037 namespace d1 {
0038 using slot_id = unsigned short;
0039 constexpr slot_id no_slot = slot_id(~0);
0040 constexpr slot_id any_slot = slot_id(~1);
0041 
0042 class task;
0043 class wait_context;
0044 class task_group_context;
0045 struct execution_data;
0046 class wait_tree_vertex_interface;
0047 class task_arena_base;
0048 }
0049 
0050 namespace d2 {
0051 class task_group;
0052 class task_group_base;
0053 }
0054 
0055 namespace r1 {
0056 //! Task spawn/wait entry points
0057 TBB_EXPORT void __TBB_EXPORTED_FUNC spawn(d1::task& t, d1::task_group_context& ctx);
0058 TBB_EXPORT void __TBB_EXPORTED_FUNC spawn(d1::task& t, d1::task_group_context& ctx, d1::slot_id id);
0059 TBB_EXPORT void __TBB_EXPORTED_FUNC execute_and_wait(d1::task& t, d1::task_group_context& t_ctx, d1::wait_context&, d1::task_group_context& w_ctx);
0060 TBB_EXPORT void __TBB_EXPORTED_FUNC wait(d1::wait_context&, d1::task_group_context& ctx);
0061 TBB_EXPORT d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::execution_data*);
0062 TBB_EXPORT d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::task_arena_base&);
0063 TBB_EXPORT d1::task_group_context* __TBB_EXPORTED_FUNC current_context();
0064 TBB_EXPORT d1::wait_tree_vertex_interface* get_thread_reference_vertex(d1::wait_tree_vertex_interface* wc);
0065 
0066 // Do not place under __TBB_RESUMABLE_TASKS. It is a stub for unsupported platforms.
0067 struct suspend_point_type;
0068 using suspend_callback_type = void(*)(void*, suspend_point_type*);
0069 //! The resumable tasks entry points
0070 TBB_EXPORT void __TBB_EXPORTED_FUNC suspend(suspend_callback_type suspend_callback, void* user_callback);
0071 TBB_EXPORT void __TBB_EXPORTED_FUNC resume(suspend_point_type* tag);
0072 TBB_EXPORT suspend_point_type* __TBB_EXPORTED_FUNC current_suspend_point();
0073 TBB_EXPORT void __TBB_EXPORTED_FUNC notify_waiters(std::uintptr_t wait_ctx_addr);
0074 
0075 class thread_data;
0076 class task_dispatcher;
0077 class external_waiter;
0078 struct task_accessor;
0079 struct task_arena_impl;
0080 } // namespace r1
0081 
0082 namespace d1 {
0083 
0084 class task_arena;
0085 using suspend_point = r1::suspend_point_type*;
0086 
0087 #if __TBB_RESUMABLE_TASKS
0088 template <typename F>
0089 static void suspend_callback(void* user_callback, suspend_point sp) {
0090     // Copy user function to a new stack after the context switch to avoid a race when the previous
0091     // suspend point is resumed while the user_callback is being called.
0092     F user_callback_copy = *static_cast<F*>(user_callback);
0093     user_callback_copy(sp);
0094 }
0095 
0096 template <typename F>
0097 void suspend(F f) {
0098     r1::suspend(&suspend_callback<F>, &f);
0099 }
0100 
0101 inline void resume(suspend_point tag) {
0102     r1::resume(tag);
0103 }
0104 #endif /* __TBB_RESUMABLE_TASKS */
0105 
0106 // TODO align wait_context on cache lane
0107 class wait_context {
0108     static constexpr std::uint64_t overflow_mask = ~((1LLU << 32) - 1);
0109 
0110     std::uint64_t m_version_and_traits{1};
0111     std::atomic<std::uint64_t> m_ref_count{};
0112 
0113     void add_reference(std::int64_t delta) {
0114         call_itt_task_notify(releasing, this);
0115         std::uint64_t r = m_ref_count.fetch_add(static_cast<std::uint64_t>(delta)) + static_cast<std::uint64_t>(delta);
0116 
0117         __TBB_ASSERT_EX((r & overflow_mask) == 0, "Overflow is detected");
0118 
0119         if (!r) {
0120             // Some external waiters or coroutine waiters sleep in wait list
0121             // Should to notify them that work is done
0122             std::uintptr_t wait_ctx_addr = std::uintptr_t(this);
0123             r1::notify_waiters(wait_ctx_addr);
0124         }
0125     }
0126 
0127     bool continue_execution() const {
0128         std::uint64_t r = m_ref_count.load(std::memory_order_acquire);
0129         __TBB_ASSERT_EX((r & overflow_mask) == 0, "Overflow is detected");
0130         return r > 0;
0131     }
0132 
0133     friend class r1::thread_data;
0134     friend class r1::task_dispatcher;
0135     friend class r1::external_waiter;
0136     friend class wait_context_vertex;
0137     friend struct r1::task_arena_impl;
0138     friend struct r1::suspend_point_type;
0139 public:
0140     // Despite the internal reference count is uin64_t we limit the user interface with uint32_t
0141     // to preserve a part of the internal reference count for special needs.
0142     wait_context(std::uint32_t ref_count) : m_ref_count{ref_count} { suppress_unused_warning(m_version_and_traits); }
0143     wait_context(const wait_context&) = delete;
0144 
0145     ~wait_context() {
0146         __TBB_ASSERT(!continue_execution(), nullptr);
0147     }
0148 
0149     void reserve(std::uint32_t delta = 1) {
0150         add_reference(delta);
0151     }
0152 
0153     void release(std::uint32_t delta = 1) {
0154         add_reference(-std::int64_t(delta));
0155     }
0156 };
0157 
0158 class wait_tree_vertex_interface {
0159 public:
0160     virtual void reserve(std::uint32_t delta = 1) = 0;
0161     virtual void release(std::uint32_t delta = 1) = 0;
0162 
0163 protected:
0164     virtual ~wait_tree_vertex_interface() = default;
0165 };
0166 
0167 class wait_context_vertex : public wait_tree_vertex_interface {
0168 public:
0169     wait_context_vertex(std::uint32_t ref = 0) : m_wait(ref) {}
0170 
0171     void reserve(std::uint32_t delta = 1) override {
0172         m_wait.reserve(delta);
0173     }
0174 
0175     void release(std::uint32_t delta = 1) override {
0176         m_wait.release(delta);
0177     }
0178 
0179     wait_context& get_context() {
0180         return m_wait;
0181     }
0182 private:
0183     friend class d2::task_group;
0184     friend class d2::task_group_base;
0185 
0186     bool continue_execution() const {
0187         return m_wait.continue_execution();
0188     }
0189 
0190     wait_context m_wait;
0191 };
0192 
0193 class reference_vertex : public wait_tree_vertex_interface {
0194 public:
0195     reference_vertex(wait_tree_vertex_interface* parent, std::uint32_t ref_count) : my_parent{parent}, m_ref_count{ref_count}
0196     {}
0197 
0198     void reserve(std::uint32_t delta = 1) override {
0199         if (m_ref_count.fetch_add(static_cast<std::uint64_t>(delta)) == 0) {
0200             my_parent->reserve();
0201         }
0202     }
0203 
0204     void release(std::uint32_t delta = 1) override {
0205         std::uint64_t ref = m_ref_count.fetch_sub(static_cast<std::uint64_t>(delta)) - static_cast<std::uint64_t>(delta);
0206         if (ref == 0) {
0207             my_parent->release();
0208         }
0209     }
0210 
0211     std::uint32_t get_num_child() {
0212         return static_cast<std::uint32_t>(m_ref_count.load(std::memory_order_acquire));
0213     }
0214 private:
0215     wait_tree_vertex_interface* my_parent;
0216     std::atomic<std::uint64_t> m_ref_count;
0217 };
0218 
0219 struct execution_data {
0220     task_group_context* context{};
0221     slot_id original_slot{};
0222     slot_id affinity_slot{};
0223 };
0224 
0225 inline task_group_context* context(const execution_data& ed) {
0226     return ed.context;
0227 }
0228 
0229 inline slot_id original_slot(const execution_data& ed) {
0230     return ed.original_slot;
0231 }
0232 
0233 inline slot_id affinity_slot(const execution_data& ed) {
0234     return ed.affinity_slot;
0235 }
0236 
0237 inline slot_id execution_slot(const execution_data& ed) {
0238     return r1::execution_slot(&ed);
0239 }
0240 
0241 inline bool is_same_affinity(const execution_data& ed) {
0242     return affinity_slot(ed) == no_slot || affinity_slot(ed) == execution_slot(ed);
0243 }
0244 
0245 inline bool is_stolen(const execution_data& ed) {
0246     return original_slot(ed) != execution_slot(ed);
0247 }
0248 
0249 inline void spawn(task& t, task_group_context& ctx) {
0250     call_itt_task_notify(releasing, &t);
0251     r1::spawn(t, ctx);
0252 }
0253 
0254 inline void spawn(task& t, task_group_context& ctx, slot_id id) {
0255     call_itt_task_notify(releasing, &t);
0256     r1::spawn(t, ctx, id);
0257 }
0258 
0259 inline void execute_and_wait(task& t, task_group_context& t_ctx, wait_context& wait_ctx, task_group_context& w_ctx) {
0260     r1::execute_and_wait(t, t_ctx, wait_ctx, w_ctx);
0261     call_itt_task_notify(acquired, &wait_ctx);
0262     call_itt_task_notify(destroy, &wait_ctx);
0263 }
0264 
0265 inline void wait(wait_context& wait_ctx, task_group_context& ctx) {
0266     r1::wait(wait_ctx, ctx);
0267     call_itt_task_notify(acquired, &wait_ctx);
0268     call_itt_task_notify(destroy, &wait_ctx);
0269 }
0270 
0271 using r1::current_context;
0272 
0273 class task_traits {
0274     std::uint64_t m_version_and_traits{};
0275     friend struct r1::task_accessor;
0276 };
0277 
0278 //! Alignment for a task object
0279 static constexpr std::size_t task_alignment = 64;
0280 
0281 //! Base class for user-defined tasks.
0282 /** @ingroup task_scheduling */
0283 class alignas(task_alignment) task : public task_traits {
0284 protected:
0285     virtual ~task() = default;
0286 
0287 public:
0288     virtual task* execute(execution_data&) = 0;
0289     virtual task* cancel(execution_data&) = 0;
0290 
0291 private:
0292     std::uint64_t m_reserved[6]{};
0293     friend struct r1::task_accessor;
0294 };
0295 static_assert(sizeof(task) == task_alignment, "task size is broken");
0296 
0297 } // namespace d1
0298 } // namespace detail
0299 } // namespace tbb
0300 
0301 #endif /* __TBB__task_H */