File indexing completed on 2025-07-30 08:46:19
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 }
0047
0048 namespace r1 {
0049
0050 TBB_EXPORT void __TBB_EXPORTED_FUNC spawn(d1::task& t, d1::task_group_context& ctx);
0051 TBB_EXPORT void __TBB_EXPORTED_FUNC spawn(d1::task& t, d1::task_group_context& ctx, d1::slot_id id);
0052 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);
0053 TBB_EXPORT void __TBB_EXPORTED_FUNC wait(d1::wait_context&, d1::task_group_context& ctx);
0054 TBB_EXPORT d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::execution_data*);
0055 TBB_EXPORT d1::task_group_context* __TBB_EXPORTED_FUNC current_context();
0056
0057
0058 struct suspend_point_type;
0059 using suspend_callback_type = void(*)(void*, suspend_point_type*);
0060
0061 TBB_EXPORT void __TBB_EXPORTED_FUNC suspend(suspend_callback_type suspend_callback, void* user_callback);
0062 TBB_EXPORT void __TBB_EXPORTED_FUNC resume(suspend_point_type* tag);
0063 TBB_EXPORT suspend_point_type* __TBB_EXPORTED_FUNC current_suspend_point();
0064 TBB_EXPORT void __TBB_EXPORTED_FUNC notify_waiters(std::uintptr_t wait_ctx_addr);
0065
0066 class thread_data;
0067 class task_dispatcher;
0068 class external_waiter;
0069 struct task_accessor;
0070 struct task_arena_impl;
0071 }
0072
0073 namespace d1 {
0074
0075 class task_arena;
0076 using suspend_point = r1::suspend_point_type*;
0077
0078 #if __TBB_RESUMABLE_TASKS
0079 template <typename F>
0080 static void suspend_callback(void* user_callback, suspend_point sp) {
0081
0082
0083 F user_callback_copy = *static_cast<F*>(user_callback);
0084 user_callback_copy(sp);
0085 }
0086
0087 template <typename F>
0088 void suspend(F f) {
0089 r1::suspend(&suspend_callback<F>, &f);
0090 }
0091
0092 inline void resume(suspend_point tag) {
0093 r1::resume(tag);
0094 }
0095 #endif
0096
0097
0098 class wait_context {
0099 static constexpr std::uint64_t overflow_mask = ~((1LLU << 32) - 1);
0100
0101 std::uint64_t m_version_and_traits{1};
0102 std::atomic<std::uint64_t> m_ref_count{};
0103
0104 void add_reference(std::int64_t delta) {
0105 call_itt_task_notify(releasing, this);
0106 std::uint64_t r = m_ref_count.fetch_add(static_cast<std::uint64_t>(delta)) + static_cast<std::uint64_t>(delta);
0107
0108 __TBB_ASSERT_EX((r & overflow_mask) == 0, "Overflow is detected");
0109
0110 if (!r) {
0111
0112
0113 std::uintptr_t wait_ctx_addr = std::uintptr_t(this);
0114 r1::notify_waiters(wait_ctx_addr);
0115 }
0116 }
0117
0118 bool continue_execution() const {
0119 std::uint64_t r = m_ref_count.load(std::memory_order_acquire);
0120 __TBB_ASSERT_EX((r & overflow_mask) == 0, "Overflow is detected");
0121 return r > 0;
0122 }
0123
0124 friend class r1::thread_data;
0125 friend class r1::task_dispatcher;
0126 friend class r1::external_waiter;
0127 friend class task_group;
0128 friend class task_group_base;
0129 friend struct r1::task_arena_impl;
0130 friend struct r1::suspend_point_type;
0131 public:
0132
0133
0134 wait_context(std::uint32_t ref_count) : m_ref_count{ref_count} { suppress_unused_warning(m_version_and_traits); }
0135 wait_context(const wait_context&) = delete;
0136
0137 ~wait_context() {
0138 __TBB_ASSERT(!continue_execution(), nullptr);
0139 }
0140
0141 void reserve(std::uint32_t delta = 1) {
0142 add_reference(delta);
0143 }
0144
0145 void release(std::uint32_t delta = 1) {
0146 add_reference(-std::int64_t(delta));
0147 }
0148 };
0149
0150 struct execution_data {
0151 task_group_context* context{};
0152 slot_id original_slot{};
0153 slot_id affinity_slot{};
0154 };
0155
0156 inline task_group_context* context(const execution_data& ed) {
0157 return ed.context;
0158 }
0159
0160 inline slot_id original_slot(const execution_data& ed) {
0161 return ed.original_slot;
0162 }
0163
0164 inline slot_id affinity_slot(const execution_data& ed) {
0165 return ed.affinity_slot;
0166 }
0167
0168 inline slot_id execution_slot(const execution_data& ed) {
0169 return r1::execution_slot(&ed);
0170 }
0171
0172 inline bool is_same_affinity(const execution_data& ed) {
0173 return affinity_slot(ed) == no_slot || affinity_slot(ed) == execution_slot(ed);
0174 }
0175
0176 inline bool is_stolen(const execution_data& ed) {
0177 return original_slot(ed) != execution_slot(ed);
0178 }
0179
0180 inline void spawn(task& t, task_group_context& ctx) {
0181 call_itt_task_notify(releasing, &t);
0182 r1::spawn(t, ctx);
0183 }
0184
0185 inline void spawn(task& t, task_group_context& ctx, slot_id id) {
0186 call_itt_task_notify(releasing, &t);
0187 r1::spawn(t, ctx, id);
0188 }
0189
0190 inline void execute_and_wait(task& t, task_group_context& t_ctx, wait_context& wait_ctx, task_group_context& w_ctx) {
0191 r1::execute_and_wait(t, t_ctx, wait_ctx, w_ctx);
0192 call_itt_task_notify(acquired, &wait_ctx);
0193 call_itt_task_notify(destroy, &wait_ctx);
0194 }
0195
0196 inline void wait(wait_context& wait_ctx, task_group_context& ctx) {
0197 r1::wait(wait_ctx, ctx);
0198 call_itt_task_notify(acquired, &wait_ctx);
0199 call_itt_task_notify(destroy, &wait_ctx);
0200 }
0201
0202 using r1::current_context;
0203
0204 class task_traits {
0205 std::uint64_t m_version_and_traits{};
0206 friend struct r1::task_accessor;
0207 };
0208
0209
0210 static constexpr std::size_t task_alignment = 64;
0211
0212
0213
0214 class alignas(task_alignment) task : public task_traits {
0215 protected:
0216 virtual ~task() = default;
0217
0218 public:
0219 virtual task* execute(execution_data&) = 0;
0220 virtual task* cancel(execution_data&) = 0;
0221
0222 private:
0223 std::uint64_t m_reserved[6]{};
0224 friend struct r1::task_accessor;
0225 };
0226 static_assert(sizeof(task) == task_alignment, "task size is broken");
0227
0228 }
0229 }
0230 }
0231
0232 #endif