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