• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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()24 void Context::ReEnqueue() { waker_->Clone(WaitReason::Unspecified()).Wake(); }
25 
GetWaker(WaitReason reason)26 Waker Context::GetWaker(WaitReason reason) { return waker_->Clone(reason); }
27 
RemoveAllWakersLocked()28 void 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)37 void Task::AddWakerLocked(Waker& waker) {
38   waker.task_ = this;
39   waker.next_ = wakers_;
40   wakers_ = &waker;
41 }
42 
RemoveWakerLocked(Waker & waker)43 void 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)57 Waker::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)67 Waker& 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()79 void 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)87 Waker 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() const99 bool Waker::IsEmpty() const {
100   std::lock_guard lock(dispatcher_lock());
101   return task_ == nullptr;
102 }
103 
InsertIntoTaskWakerList()104 void Waker::InsertIntoTaskWakerList() {
105   std::lock_guard lock(dispatcher_lock());
106   InsertIntoTaskWakerListLocked();
107 }
108 
InsertIntoTaskWakerListLocked()109 void Waker::InsertIntoTaskWakerListLocked() {
110   if (task_ != nullptr) {
111     task_->AddWakerLocked(*this);
112   }
113 }
114 
RemoveFromTaskWakerList()115 void Waker::RemoveFromTaskWakerList() {
116   std::lock_guard lock(dispatcher_lock());
117   RemoveFromTaskWakerListLocked();
118 }
119 
RemoveFromTaskWakerListLocked()120 void Waker::RemoveFromTaskWakerListLocked() {
121   if (task_ != nullptr) {
122     task_->RemoveWakerLocked(*this);
123   }
124 }
125 
Deregister()126 void 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)135 void 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)147 void 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)158 void 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)168 void DispatcherBase::RemoveSleepingTaskLocked(Task& task) {
169   RemoveTaskFromList(task);
170   if (sleeping_ == &task) {
171     sleeping_ = task.next_;
172   }
173 }
174 
AddTaskToWokenList(Task & task)175 void 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)185 void 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)193 void 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()223 Task* 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