File indexing completed on 2025-12-18 10:24:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
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
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
0067 struct suspend_point_type;
0068 using suspend_callback_type = void(*)(void*, suspend_point_type*);
0069
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 }
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
0091
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
0105
0106
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
0121
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
0141
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
0279 static constexpr std::size_t task_alignment = 64;
0280
0281
0282
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 }
0298 }
0299 }
0300
0301 #endif