xref: /oneTBB/src/tbb/waiters.h (revision 68e075cb)
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_waiters_H
18 #define _TBB_waiters_H
19 
20 #include "oneapi/tbb/detail/_task.h"
21 #include "scheduler_common.h"
22 #include "arena.h"
23 
24 namespace tbb {
25 namespace detail {
26 namespace r1 {
27 
28 inline d1::task* get_self_recall_task(arena_slot& slot);
29 
30 class waiter_base {
31 public:
32     waiter_base(arena& a, int yields_multiplier = 1) : my_arena(a), my_backoff(int(a.my_num_slots), yields_multiplier) {}
33 
34     bool pause() {
35         if (my_backoff.pause()) {
36             my_arena.is_out_of_work();
37             return true;
38         }
39 
40         return false;
41     }
42 
43     void reset_wait() {
44         my_backoff.reset_wait();
45     }
46 
47 protected:
48     arena& my_arena;
49     stealing_loop_backoff my_backoff;
50 };
51 
52 class outermost_worker_waiter : public waiter_base {
53 public:
54     using waiter_base::waiter_base;
55 
56     bool continue_execution(arena_slot& slot, d1::task*& t) const {
57         __TBB_ASSERT(t == nullptr, nullptr);
58 
59         if (is_worker_should_leave(slot)) {
60             // Leave dispatch loop
61             return false;
62         }
63 
64         t = get_self_recall_task(slot);
65         return true;
66     }
67 
68     void pause(arena_slot&) {
69         waiter_base::pause();
70     }
71 
72 
73     d1::wait_context* wait_ctx() {
74         return nullptr;
75     }
76 
77     static bool postpone_execution(d1::task&) {
78         return false;
79     }
80 
81 private:
82     using base_type = waiter_base;
83 
84     bool is_worker_should_leave(arena_slot& slot) const {
85         bool is_top_priority_arena = my_arena.my_is_top_priority.load(std::memory_order_relaxed);
86         bool is_task_pool_empty = slot.task_pool.load(std::memory_order_relaxed) == EmptyTaskPool;
87 
88         if (is_top_priority_arena) {
89             // Worker in most priority arena do not leave arena, until all work in task_pool is done
90             if (is_task_pool_empty && my_arena.is_recall_requested()) {
91                 return true;
92             }
93         } else {
94             if (my_arena.is_recall_requested()) {
95                 // If worker has work in task pool, we must notify other threads,
96                 // because can appear missed wake up of other threads
97                 if (!is_task_pool_empty) {
98                     my_arena.advertise_new_work<arena::wakeup>();
99                 }
100                 return true;
101             }
102         }
103 
104         return false;
105     }
106 };
107 
108 class sleep_waiter : public waiter_base {
109 protected:
110     using waiter_base::waiter_base;
111 
112     bool is_arena_empty() {
113         return my_arena.my_pool_state.load(std::memory_order_relaxed) == arena::SNAPSHOT_EMPTY;
114     }
115 
116     template <typename Pred>
117     void sleep(std::uintptr_t uniq_tag, Pred wakeup_condition) {
118         my_arena.my_market->get_wait_list().wait<market_concurrent_monitor::thread_context>(wakeup_condition,
119             market_context{uniq_tag, &my_arena});
120     }
121 };
122 
123 class external_waiter : public sleep_waiter {
124 public:
125     external_waiter(arena& a, d1::wait_context& wo)
126         : sleep_waiter(a, /*yields_multiplier*/10), my_wait_ctx(wo)
127         {}
128 
129     bool continue_execution(arena_slot& slot, d1::task*& t) const {
130         __TBB_ASSERT(t == nullptr, nullptr);
131         if (!my_wait_ctx.continue_execution())
132             return false;
133         t = get_self_recall_task(slot);
134         return true;
135     }
136 
137     void pause(arena_slot&) {
138         if (!sleep_waiter::pause()) {
139             return;
140         }
141 
142         auto wakeup_condition = [&] { return !is_arena_empty() || !my_wait_ctx.continue_execution(); };
143 
144         sleep(std::uintptr_t(&my_wait_ctx), wakeup_condition);
145         my_backoff.reset_wait();
146     }
147 
148     d1::wait_context* wait_ctx() {
149         return &my_wait_ctx;
150     }
151 
152     static bool postpone_execution(d1::task&) {
153         return false;
154     }
155 
156 private:
157     d1::wait_context& my_wait_ctx;
158 };
159 
160 #if __TBB_RESUMABLE_TASKS
161 
162 class coroutine_waiter : public sleep_waiter {
163 public:
164     using sleep_waiter::sleep_waiter;
165 
166     bool continue_execution(arena_slot& slot, d1::task*& t) const {
167         __TBB_ASSERT(t == nullptr, nullptr);
168         t = get_self_recall_task(slot);
169         return true;
170     }
171 
172     void pause(arena_slot& slot) {
173         if (!sleep_waiter::pause()) {
174             return;
175         }
176 
177         suspend_point_type* sp = slot.default_task_dispatcher().m_suspend_point;
178 
179         auto wakeup_condition = [&] { return !is_arena_empty() || sp->m_is_owner_recalled.load(std::memory_order_relaxed); };
180 
181         sleep(std::uintptr_t(sp), wakeup_condition);
182         my_backoff.reset_wait();
183     }
184 
185     void reset_wait() {
186         my_backoff.reset_wait();
187     }
188 
189     d1::wait_context* wait_ctx() {
190         return nullptr;
191     }
192 
193     static bool postpone_execution(d1::task& t) {
194         return task_accessor::is_resume_task(t);
195     }
196 };
197 
198 #endif // __TBB_RESUMABLE_TASKS
199 
200 } // namespace r1
201 } // namespace detail
202 } // namespace tbb
203 
204 #endif // _TBB_waiters_H
205