1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #include "pw_async2/dispatcher_base.h" 16 17 #include <mutex> 18 19 #include "pw_assert/check.h" 20 #include "pw_sync/lock_annotations.h" 21 22 namespace pw::async2 { 23 ReEnqueue()24void Context::ReEnqueue() { waker_->Clone(WaitReason::Unspecified()).Wake(); } 25 GetWaker(WaitReason reason)26Waker Context::GetWaker(WaitReason reason) { return waker_->Clone(reason); } 27 RemoveAllWakersLocked()28void Task::RemoveAllWakersLocked() { 29 while (wakers_ != nullptr) { 30 Waker* current = wakers_; 31 wakers_ = current->next_; 32 current->task_ = nullptr; 33 current->next_ = nullptr; 34 } 35 } 36 AddWakerLocked(Waker & waker)37void Task::AddWakerLocked(Waker& waker) { 38 waker.task_ = this; 39 waker.next_ = wakers_; 40 wakers_ = &waker; 41 } 42 RemoveWakerLocked(Waker & waker)43void Task::RemoveWakerLocked(Waker& waker) { 44 if (&waker == wakers_) { 45 wakers_ = wakers_->next_; 46 } else { 47 Waker* current = wakers_; 48 while (current->next_ != &waker) { 49 current = current->next_; 50 } 51 current->next_ = current->next_->next_; 52 } 53 waker.task_ = nullptr; 54 waker.next_ = nullptr; 55 } 56 Waker(Waker && other)57Waker::Waker(Waker&& other) noexcept { 58 std::lock_guard lock(dispatcher_lock()); 59 if (other.task_ == nullptr) { 60 return; 61 } 62 Task& task = *other.task_; 63 task.RemoveWakerLocked(other); 64 task.AddWakerLocked(*this); 65 } 66 operator =(Waker && other)67Waker& Waker::operator=(Waker&& other) noexcept { 68 std::lock_guard lock(dispatcher_lock()); 69 RemoveFromTaskWakerListLocked(); 70 if (other.task_ == nullptr) { 71 return *this; 72 } 73 Task& task = *other.task_; 74 task.RemoveWakerLocked(other); 75 task.AddWakerLocked(*this); 76 return *this; 77 } 78 Wake()79void Waker::Wake() && { 80 std::lock_guard lock(dispatcher_lock()); 81 if (task_ != nullptr) { 82 task_->dispatcher_->WakeTask(*task_); 83 RemoveFromTaskWakerListLocked(); 84 } 85 } 86 Clone(WaitReason)87Waker Waker::Clone(WaitReason) & { 88 Waker waker; 89 { 90 std::lock_guard lock(dispatcher_lock()); 91 if (task_ != nullptr) { 92 waker.task_ = task_; 93 task_->AddWakerLocked(waker); 94 } 95 } 96 return waker; 97 } 98 IsEmpty() const99bool Waker::IsEmpty() const { 100 std::lock_guard lock(dispatcher_lock()); 101 return task_ == nullptr; 102 } 103 InsertIntoTaskWakerList()104void Waker::InsertIntoTaskWakerList() { 105 std::lock_guard lock(dispatcher_lock()); 106 InsertIntoTaskWakerListLocked(); 107 } 108 InsertIntoTaskWakerListLocked()109void Waker::InsertIntoTaskWakerListLocked() { 110 if (task_ != nullptr) { 111 task_->AddWakerLocked(*this); 112 } 113 } 114 RemoveFromTaskWakerList()115void Waker::RemoveFromTaskWakerList() { 116 std::lock_guard lock(dispatcher_lock()); 117 RemoveFromTaskWakerListLocked(); 118 } 119 RemoveFromTaskWakerListLocked()120void Waker::RemoveFromTaskWakerListLocked() { 121 if (task_ != nullptr) { 122 task_->RemoveWakerLocked(*this); 123 } 124 } 125 Deregister()126void DispatcherBase::Deregister() { 127 std::lock_guard lock(dispatcher_lock()); 128 UnpostTaskList(first_woken_); 129 first_woken_ = nullptr; 130 last_woken_ = nullptr; 131 UnpostTaskList(sleeping_); 132 sleeping_ = nullptr; 133 } 134 UnpostTaskList(Task * task)135void DispatcherBase::UnpostTaskList(Task* task) { 136 while (task != nullptr) { 137 task->state_ = Task::State::kUnposted; 138 task->dispatcher_ = nullptr; 139 task->prev_ = nullptr; 140 Task* next = task->next_; 141 task->next_ = nullptr; 142 task->RemoveAllWakersLocked(); 143 task = next; 144 } 145 } 146 RemoveTaskFromList(Task & task)147void DispatcherBase::RemoveTaskFromList(Task& task) { 148 if (task.prev_ != nullptr) { 149 task.prev_->next_ = task.next_; 150 } 151 if (task.next_ != nullptr) { 152 task.next_->prev_ = task.prev_; 153 } 154 task.prev_ = nullptr; 155 task.next_ = nullptr; 156 } 157 RemoveWokenTaskLocked(Task & task)158void DispatcherBase::RemoveWokenTaskLocked(Task& task) { 159 RemoveTaskFromList(task); 160 if (first_woken_ == &task) { 161 first_woken_ = task.next_; 162 } 163 if (last_woken_ == &task) { 164 last_woken_ = task.prev_; 165 } 166 } 167 RemoveSleepingTaskLocked(Task & task)168void DispatcherBase::RemoveSleepingTaskLocked(Task& task) { 169 RemoveTaskFromList(task); 170 if (sleeping_ == &task) { 171 sleeping_ = task.next_; 172 } 173 } 174 AddTaskToWokenList(Task & task)175void DispatcherBase::AddTaskToWokenList(Task& task) { 176 if (first_woken_ == nullptr) { 177 first_woken_ = &task; 178 } else { 179 last_woken_->next_ = &task; 180 task.prev_ = last_woken_; 181 } 182 last_woken_ = &task; 183 } 184 AddTaskToSleepingList(Task & task)185void DispatcherBase::AddTaskToSleepingList(Task& task) { 186 if (sleeping_ != nullptr) { 187 sleeping_->prev_ = &task; 188 } 189 task.next_ = sleeping_; 190 sleeping_ = &task; 191 } 192 WakeTask(Task & task)193void DispatcherBase::WakeTask(Task& task) { 194 switch (task.state_) { 195 case Task::State::kWoken: 196 // Do nothing-- this has already been woken. 197 return; 198 case Task::State::kUnposted: 199 // This should be unreachable. 200 PW_CHECK(false); 201 case Task::State::kRunning: 202 // Wake again to indicate that this task should be run once more, 203 // as the state of the world may have changed since the task 204 // started running. 205 break; 206 case Task::State::kSleeping: 207 RemoveSleepingTaskLocked(task); 208 // Wake away! 209 break; 210 } 211 task.state_ = Task::State::kWoken; 212 AddTaskToWokenList(task); 213 if (wants_wake_) { 214 // Note: it's quite annoying to make this call under the lock, as it can 215 // result in extra thread wakeup/sleep cycles. 216 // 217 // However, releasing the lock first would allow for the possibility that 218 // the ``Dispatcher`` has been destroyed, making the call invalid. 219 DoWake(); 220 } 221 } 222 PopWokenTask()223Task* DispatcherBase::PopWokenTask() { 224 if (first_woken_ == nullptr) { 225 return nullptr; 226 } 227 Task& task = *first_woken_; 228 if (task.next_ != nullptr) { 229 task.next_->prev_ = nullptr; 230 } else { 231 last_woken_ = nullptr; 232 } 233 first_woken_ = task.next_; 234 task.prev_ = nullptr; 235 task.next_ = nullptr; 236 return &task; 237 } 238 239 } // namespace pw::async2 240