1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_async2/dispatcher_base.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <mutex>
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/lock_annotations.h"
21*61c4878aSAndroid Build Coastguard Worker
22*61c4878aSAndroid Build Coastguard Worker namespace pw::async2 {
23*61c4878aSAndroid Build Coastguard Worker
ReEnqueue()24*61c4878aSAndroid Build Coastguard Worker void Context::ReEnqueue() {
25*61c4878aSAndroid Build Coastguard Worker Waker waker;
26*61c4878aSAndroid Build Coastguard Worker waker_->InternalCloneInto(waker);
27*61c4878aSAndroid Build Coastguard Worker std::move(waker).Wake();
28*61c4878aSAndroid Build Coastguard Worker }
29*61c4878aSAndroid Build Coastguard Worker
InternalStoreWaker(Waker & waker_out)30*61c4878aSAndroid Build Coastguard Worker void Context::InternalStoreWaker(Waker& waker_out) {
31*61c4878aSAndroid Build Coastguard Worker waker_->InternalCloneInto(waker_out);
32*61c4878aSAndroid Build Coastguard Worker }
33*61c4878aSAndroid Build Coastguard Worker
RemoveAllWakersLocked()34*61c4878aSAndroid Build Coastguard Worker void Task::RemoveAllWakersLocked() {
35*61c4878aSAndroid Build Coastguard Worker while (wakers_ != nullptr) {
36*61c4878aSAndroid Build Coastguard Worker Waker* current = wakers_;
37*61c4878aSAndroid Build Coastguard Worker wakers_ = current->next_;
38*61c4878aSAndroid Build Coastguard Worker current->task_ = nullptr;
39*61c4878aSAndroid Build Coastguard Worker current->next_ = nullptr;
40*61c4878aSAndroid Build Coastguard Worker }
41*61c4878aSAndroid Build Coastguard Worker }
42*61c4878aSAndroid Build Coastguard Worker
AddWakerLocked(Waker & waker)43*61c4878aSAndroid Build Coastguard Worker void Task::AddWakerLocked(Waker& waker) {
44*61c4878aSAndroid Build Coastguard Worker waker.task_ = this;
45*61c4878aSAndroid Build Coastguard Worker waker.next_ = wakers_;
46*61c4878aSAndroid Build Coastguard Worker wakers_ = &waker;
47*61c4878aSAndroid Build Coastguard Worker }
48*61c4878aSAndroid Build Coastguard Worker
RemoveWakerLocked(Waker & waker)49*61c4878aSAndroid Build Coastguard Worker void Task::RemoveWakerLocked(Waker& waker) {
50*61c4878aSAndroid Build Coastguard Worker if (&waker == wakers_) {
51*61c4878aSAndroid Build Coastguard Worker wakers_ = wakers_->next_;
52*61c4878aSAndroid Build Coastguard Worker } else {
53*61c4878aSAndroid Build Coastguard Worker Waker* current = wakers_;
54*61c4878aSAndroid Build Coastguard Worker while (current->next_ != &waker) {
55*61c4878aSAndroid Build Coastguard Worker current = current->next_;
56*61c4878aSAndroid Build Coastguard Worker }
57*61c4878aSAndroid Build Coastguard Worker current->next_ = current->next_->next_;
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker waker.task_ = nullptr;
60*61c4878aSAndroid Build Coastguard Worker waker.next_ = nullptr;
61*61c4878aSAndroid Build Coastguard Worker }
62*61c4878aSAndroid Build Coastguard Worker
IsRegistered() const63*61c4878aSAndroid Build Coastguard Worker bool Task::IsRegistered() const {
64*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
65*61c4878aSAndroid Build Coastguard Worker return state_ != Task::State::kUnposted;
66*61c4878aSAndroid Build Coastguard Worker }
67*61c4878aSAndroid Build Coastguard Worker
Deregister()68*61c4878aSAndroid Build Coastguard Worker void Task::Deregister() {
69*61c4878aSAndroid Build Coastguard Worker pw::sync::Mutex* task_execution_lock;
70*61c4878aSAndroid Build Coastguard Worker {
71*61c4878aSAndroid Build Coastguard Worker // Fast path: the task is not running.
72*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
73*61c4878aSAndroid Build Coastguard Worker if (TryDeregister()) {
74*61c4878aSAndroid Build Coastguard Worker return;
75*61c4878aSAndroid Build Coastguard Worker }
76*61c4878aSAndroid Build Coastguard Worker // The task was running, so we have to wait for the task to stop being
77*61c4878aSAndroid Build Coastguard Worker // run by acquiring the `task_lock`.
78*61c4878aSAndroid Build Coastguard Worker task_execution_lock = &dispatcher_->task_execution_lock_;
79*61c4878aSAndroid Build Coastguard Worker }
80*61c4878aSAndroid Build Coastguard Worker
81*61c4878aSAndroid Build Coastguard Worker // NOTE: there is a race here where `task_execution_lock_` may be
82*61c4878aSAndroid Build Coastguard Worker // invalidated by concurrent destruction of the dispatcher.
83*61c4878aSAndroid Build Coastguard Worker //
84*61c4878aSAndroid Build Coastguard Worker // This restriction is documented above, but is still fairly footgun-y.
85*61c4878aSAndroid Build Coastguard Worker std::lock_guard task_lock(*task_execution_lock);
86*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
87*61c4878aSAndroid Build Coastguard Worker PW_CHECK(TryDeregister());
88*61c4878aSAndroid Build Coastguard Worker }
89*61c4878aSAndroid Build Coastguard Worker
TryDeregister()90*61c4878aSAndroid Build Coastguard Worker bool Task::TryDeregister() {
91*61c4878aSAndroid Build Coastguard Worker switch (state_) {
92*61c4878aSAndroid Build Coastguard Worker case Task::State::kUnposted:
93*61c4878aSAndroid Build Coastguard Worker return true;
94*61c4878aSAndroid Build Coastguard Worker case Task::State::kSleeping:
95*61c4878aSAndroid Build Coastguard Worker dispatcher_->RemoveSleepingTaskLocked(*this);
96*61c4878aSAndroid Build Coastguard Worker break;
97*61c4878aSAndroid Build Coastguard Worker case Task::State::kRunning:
98*61c4878aSAndroid Build Coastguard Worker return false;
99*61c4878aSAndroid Build Coastguard Worker case Task::State::kWoken:
100*61c4878aSAndroid Build Coastguard Worker dispatcher_->RemoveWokenTaskLocked(*this);
101*61c4878aSAndroid Build Coastguard Worker break;
102*61c4878aSAndroid Build Coastguard Worker }
103*61c4878aSAndroid Build Coastguard Worker state_ = Task::State::kUnposted;
104*61c4878aSAndroid Build Coastguard Worker RemoveAllWakersLocked();
105*61c4878aSAndroid Build Coastguard Worker
106*61c4878aSAndroid Build Coastguard Worker // Wake the dispatcher up if this was the last task so that it can see that
107*61c4878aSAndroid Build Coastguard Worker // all tasks have completed.
108*61c4878aSAndroid Build Coastguard Worker if (dispatcher_->first_woken_ == nullptr &&
109*61c4878aSAndroid Build Coastguard Worker dispatcher_->sleeping_ == nullptr && dispatcher_->wants_wake_) {
110*61c4878aSAndroid Build Coastguard Worker dispatcher_->DoWake();
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker dispatcher_ = nullptr;
113*61c4878aSAndroid Build Coastguard Worker return true;
114*61c4878aSAndroid Build Coastguard Worker }
115*61c4878aSAndroid Build Coastguard Worker
Waker(Waker && other)116*61c4878aSAndroid Build Coastguard Worker Waker::Waker(Waker&& other) noexcept {
117*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
118*61c4878aSAndroid Build Coastguard Worker if (other.task_ == nullptr) {
119*61c4878aSAndroid Build Coastguard Worker return;
120*61c4878aSAndroid Build Coastguard Worker }
121*61c4878aSAndroid Build Coastguard Worker Task& task = *other.task_;
122*61c4878aSAndroid Build Coastguard Worker task.RemoveWakerLocked(other);
123*61c4878aSAndroid Build Coastguard Worker task.AddWakerLocked(*this);
124*61c4878aSAndroid Build Coastguard Worker }
125*61c4878aSAndroid Build Coastguard Worker
operator =(Waker && other)126*61c4878aSAndroid Build Coastguard Worker Waker& Waker::operator=(Waker&& other) noexcept {
127*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
128*61c4878aSAndroid Build Coastguard Worker RemoveFromTaskWakerListLocked();
129*61c4878aSAndroid Build Coastguard Worker if (other.task_ == nullptr) {
130*61c4878aSAndroid Build Coastguard Worker return *this;
131*61c4878aSAndroid Build Coastguard Worker }
132*61c4878aSAndroid Build Coastguard Worker Task& task = *other.task_;
133*61c4878aSAndroid Build Coastguard Worker task.RemoveWakerLocked(other);
134*61c4878aSAndroid Build Coastguard Worker task.AddWakerLocked(*this);
135*61c4878aSAndroid Build Coastguard Worker return *this;
136*61c4878aSAndroid Build Coastguard Worker }
137*61c4878aSAndroid Build Coastguard Worker
Wake()138*61c4878aSAndroid Build Coastguard Worker void Waker::Wake() && {
139*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
140*61c4878aSAndroid Build Coastguard Worker if (task_ != nullptr) {
141*61c4878aSAndroid Build Coastguard Worker task_->dispatcher_->WakeTask(*task_);
142*61c4878aSAndroid Build Coastguard Worker RemoveFromTaskWakerListLocked();
143*61c4878aSAndroid Build Coastguard Worker }
144*61c4878aSAndroid Build Coastguard Worker }
145*61c4878aSAndroid Build Coastguard Worker
InternalCloneInto(Waker & out)146*61c4878aSAndroid Build Coastguard Worker void Waker::InternalCloneInto(Waker& out) & {
147*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
148*61c4878aSAndroid Build Coastguard Worker // The `out` waker already points to this task, so no work is necessary.
149*61c4878aSAndroid Build Coastguard Worker if (out.task_ == task_) {
150*61c4878aSAndroid Build Coastguard Worker return;
151*61c4878aSAndroid Build Coastguard Worker }
152*61c4878aSAndroid Build Coastguard Worker // Remove the output waker from its existing task's list.
153*61c4878aSAndroid Build Coastguard Worker out.RemoveFromTaskWakerListLocked();
154*61c4878aSAndroid Build Coastguard Worker out.task_ = task_;
155*61c4878aSAndroid Build Coastguard Worker // Only add if the waker being cloned is actually associated with a task.
156*61c4878aSAndroid Build Coastguard Worker if (task_ != nullptr) {
157*61c4878aSAndroid Build Coastguard Worker task_->AddWakerLocked(out);
158*61c4878aSAndroid Build Coastguard Worker }
159*61c4878aSAndroid Build Coastguard Worker }
160*61c4878aSAndroid Build Coastguard Worker
IsEmpty() const161*61c4878aSAndroid Build Coastguard Worker bool Waker::IsEmpty() const {
162*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
163*61c4878aSAndroid Build Coastguard Worker return task_ == nullptr;
164*61c4878aSAndroid Build Coastguard Worker }
165*61c4878aSAndroid Build Coastguard Worker
InsertIntoTaskWakerList()166*61c4878aSAndroid Build Coastguard Worker void Waker::InsertIntoTaskWakerList() {
167*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
168*61c4878aSAndroid Build Coastguard Worker InsertIntoTaskWakerListLocked();
169*61c4878aSAndroid Build Coastguard Worker }
170*61c4878aSAndroid Build Coastguard Worker
InsertIntoTaskWakerListLocked()171*61c4878aSAndroid Build Coastguard Worker void Waker::InsertIntoTaskWakerListLocked() {
172*61c4878aSAndroid Build Coastguard Worker if (task_ != nullptr) {
173*61c4878aSAndroid Build Coastguard Worker task_->AddWakerLocked(*this);
174*61c4878aSAndroid Build Coastguard Worker }
175*61c4878aSAndroid Build Coastguard Worker }
176*61c4878aSAndroid Build Coastguard Worker
RemoveFromTaskWakerList()177*61c4878aSAndroid Build Coastguard Worker void Waker::RemoveFromTaskWakerList() {
178*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
179*61c4878aSAndroid Build Coastguard Worker RemoveFromTaskWakerListLocked();
180*61c4878aSAndroid Build Coastguard Worker }
181*61c4878aSAndroid Build Coastguard Worker
RemoveFromTaskWakerListLocked()182*61c4878aSAndroid Build Coastguard Worker void Waker::RemoveFromTaskWakerListLocked() {
183*61c4878aSAndroid Build Coastguard Worker if (task_ != nullptr) {
184*61c4878aSAndroid Build Coastguard Worker task_->RemoveWakerLocked(*this);
185*61c4878aSAndroid Build Coastguard Worker }
186*61c4878aSAndroid Build Coastguard Worker }
187*61c4878aSAndroid Build Coastguard Worker
Deregister()188*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::Deregister() {
189*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
190*61c4878aSAndroid Build Coastguard Worker UnpostTaskList(first_woken_);
191*61c4878aSAndroid Build Coastguard Worker first_woken_ = nullptr;
192*61c4878aSAndroid Build Coastguard Worker last_woken_ = nullptr;
193*61c4878aSAndroid Build Coastguard Worker UnpostTaskList(sleeping_);
194*61c4878aSAndroid Build Coastguard Worker sleeping_ = nullptr;
195*61c4878aSAndroid Build Coastguard Worker }
196*61c4878aSAndroid Build Coastguard Worker
Post(Task & task)197*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::Post(Task& task) {
198*61c4878aSAndroid Build Coastguard Worker bool wake_dispatcher = false;
199*61c4878aSAndroid Build Coastguard Worker {
200*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
201*61c4878aSAndroid Build Coastguard Worker PW_DASSERT(task.state_ == Task::State::kUnposted);
202*61c4878aSAndroid Build Coastguard Worker PW_DASSERT(task.dispatcher_ == nullptr);
203*61c4878aSAndroid Build Coastguard Worker task.state_ = Task::State::kWoken;
204*61c4878aSAndroid Build Coastguard Worker task.dispatcher_ = this;
205*61c4878aSAndroid Build Coastguard Worker AddTaskToWokenList(task);
206*61c4878aSAndroid Build Coastguard Worker if (wants_wake_) {
207*61c4878aSAndroid Build Coastguard Worker wake_dispatcher = true;
208*61c4878aSAndroid Build Coastguard Worker wants_wake_ = false;
209*61c4878aSAndroid Build Coastguard Worker }
210*61c4878aSAndroid Build Coastguard Worker }
211*61c4878aSAndroid Build Coastguard Worker // Note: unlike in ``WakeTask``, here we know that the ``Dispatcher`` will
212*61c4878aSAndroid Build Coastguard Worker // not be destroyed out from under our feet because we're in a method being
213*61c4878aSAndroid Build Coastguard Worker // called on the ``Dispatcher`` by a user.
214*61c4878aSAndroid Build Coastguard Worker if (wake_dispatcher) {
215*61c4878aSAndroid Build Coastguard Worker DoWake();
216*61c4878aSAndroid Build Coastguard Worker }
217*61c4878aSAndroid Build Coastguard Worker }
218*61c4878aSAndroid Build Coastguard Worker
AttemptRequestWake(bool allow_empty)219*61c4878aSAndroid Build Coastguard Worker NativeDispatcherBase::SleepInfo NativeDispatcherBase::AttemptRequestWake(
220*61c4878aSAndroid Build Coastguard Worker bool allow_empty) {
221*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
222*61c4878aSAndroid Build Coastguard Worker // Don't allow sleeping if there are already tasks waiting to be run.
223*61c4878aSAndroid Build Coastguard Worker if (first_woken_ != nullptr) {
224*61c4878aSAndroid Build Coastguard Worker return SleepInfo::DontSleep();
225*61c4878aSAndroid Build Coastguard Worker }
226*61c4878aSAndroid Build Coastguard Worker if (!allow_empty && sleeping_ == nullptr) {
227*61c4878aSAndroid Build Coastguard Worker return SleepInfo::DontSleep();
228*61c4878aSAndroid Build Coastguard Worker }
229*61c4878aSAndroid Build Coastguard Worker /// Indicate that the ``Dispatcher`` is sleeping and will need a ``DoWake``
230*61c4878aSAndroid Build Coastguard Worker /// call once more work can be done.
231*61c4878aSAndroid Build Coastguard Worker wants_wake_ = true;
232*61c4878aSAndroid Build Coastguard Worker // Once timers are added, this should check them.
233*61c4878aSAndroid Build Coastguard Worker return SleepInfo::Indefinitely();
234*61c4878aSAndroid Build Coastguard Worker }
235*61c4878aSAndroid Build Coastguard Worker
RunOneTask(Dispatcher & dispatcher,Task * task_to_look_for)236*61c4878aSAndroid Build Coastguard Worker NativeDispatcherBase::RunOneTaskResult NativeDispatcherBase::RunOneTask(
237*61c4878aSAndroid Build Coastguard Worker Dispatcher& dispatcher, Task* task_to_look_for) {
238*61c4878aSAndroid Build Coastguard Worker std::lock_guard task_lock(task_execution_lock_);
239*61c4878aSAndroid Build Coastguard Worker Task* task;
240*61c4878aSAndroid Build Coastguard Worker {
241*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
242*61c4878aSAndroid Build Coastguard Worker task = PopWokenTask();
243*61c4878aSAndroid Build Coastguard Worker if (task == nullptr) {
244*61c4878aSAndroid Build Coastguard Worker bool all_complete = first_woken_ == nullptr && sleeping_ == nullptr;
245*61c4878aSAndroid Build Coastguard Worker return RunOneTaskResult(
246*61c4878aSAndroid Build Coastguard Worker /*completed_all_tasks=*/all_complete,
247*61c4878aSAndroid Build Coastguard Worker /*completed_main_task=*/false,
248*61c4878aSAndroid Build Coastguard Worker /*ran_a_task=*/false);
249*61c4878aSAndroid Build Coastguard Worker }
250*61c4878aSAndroid Build Coastguard Worker task->state_ = Task::State::kRunning;
251*61c4878aSAndroid Build Coastguard Worker }
252*61c4878aSAndroid Build Coastguard Worker
253*61c4878aSAndroid Build Coastguard Worker bool complete;
254*61c4878aSAndroid Build Coastguard Worker {
255*61c4878aSAndroid Build Coastguard Worker Waker waker(*task);
256*61c4878aSAndroid Build Coastguard Worker Context context(dispatcher, waker);
257*61c4878aSAndroid Build Coastguard Worker complete = task->Pend(context).IsReady();
258*61c4878aSAndroid Build Coastguard Worker }
259*61c4878aSAndroid Build Coastguard Worker if (complete) {
260*61c4878aSAndroid Build Coastguard Worker bool all_complete;
261*61c4878aSAndroid Build Coastguard Worker {
262*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
263*61c4878aSAndroid Build Coastguard Worker switch (task->state_) {
264*61c4878aSAndroid Build Coastguard Worker case Task::State::kUnposted:
265*61c4878aSAndroid Build Coastguard Worker case Task::State::kSleeping:
266*61c4878aSAndroid Build Coastguard Worker PW_DASSERT(false);
267*61c4878aSAndroid Build Coastguard Worker PW_UNREACHABLE;
268*61c4878aSAndroid Build Coastguard Worker case Task::State::kRunning:
269*61c4878aSAndroid Build Coastguard Worker break;
270*61c4878aSAndroid Build Coastguard Worker case Task::State::kWoken:
271*61c4878aSAndroid Build Coastguard Worker RemoveWokenTaskLocked(*task);
272*61c4878aSAndroid Build Coastguard Worker break;
273*61c4878aSAndroid Build Coastguard Worker }
274*61c4878aSAndroid Build Coastguard Worker task->state_ = Task::State::kUnposted;
275*61c4878aSAndroid Build Coastguard Worker task->dispatcher_ = nullptr;
276*61c4878aSAndroid Build Coastguard Worker task->RemoveAllWakersLocked();
277*61c4878aSAndroid Build Coastguard Worker all_complete = first_woken_ == nullptr && sleeping_ == nullptr;
278*61c4878aSAndroid Build Coastguard Worker }
279*61c4878aSAndroid Build Coastguard Worker task->DoDestroy();
280*61c4878aSAndroid Build Coastguard Worker return RunOneTaskResult(
281*61c4878aSAndroid Build Coastguard Worker /*completed_all_tasks=*/all_complete,
282*61c4878aSAndroid Build Coastguard Worker /*completed_main_task=*/task == task_to_look_for,
283*61c4878aSAndroid Build Coastguard Worker /*ran_a_task=*/true);
284*61c4878aSAndroid Build Coastguard Worker } else {
285*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(dispatcher_lock());
286*61c4878aSAndroid Build Coastguard Worker if (task->state_ == Task::State::kRunning) {
287*61c4878aSAndroid Build Coastguard Worker task->state_ = Task::State::kSleeping;
288*61c4878aSAndroid Build Coastguard Worker AddTaskToSleepingList(*task);
289*61c4878aSAndroid Build Coastguard Worker }
290*61c4878aSAndroid Build Coastguard Worker return RunOneTaskResult(
291*61c4878aSAndroid Build Coastguard Worker /*completed_all_tasks=*/false,
292*61c4878aSAndroid Build Coastguard Worker /*completed_main_task=*/false,
293*61c4878aSAndroid Build Coastguard Worker /*ran_a_task=*/true);
294*61c4878aSAndroid Build Coastguard Worker }
295*61c4878aSAndroid Build Coastguard Worker }
296*61c4878aSAndroid Build Coastguard Worker
UnpostTaskList(Task * task)297*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::UnpostTaskList(Task* task) {
298*61c4878aSAndroid Build Coastguard Worker while (task != nullptr) {
299*61c4878aSAndroid Build Coastguard Worker task->state_ = Task::State::kUnposted;
300*61c4878aSAndroid Build Coastguard Worker task->dispatcher_ = nullptr;
301*61c4878aSAndroid Build Coastguard Worker task->prev_ = nullptr;
302*61c4878aSAndroid Build Coastguard Worker Task* next = task->next_;
303*61c4878aSAndroid Build Coastguard Worker task->next_ = nullptr;
304*61c4878aSAndroid Build Coastguard Worker task->RemoveAllWakersLocked();
305*61c4878aSAndroid Build Coastguard Worker task = next;
306*61c4878aSAndroid Build Coastguard Worker }
307*61c4878aSAndroid Build Coastguard Worker }
308*61c4878aSAndroid Build Coastguard Worker
RemoveTaskFromList(Task & task)309*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::RemoveTaskFromList(Task& task) {
310*61c4878aSAndroid Build Coastguard Worker if (task.prev_ != nullptr) {
311*61c4878aSAndroid Build Coastguard Worker task.prev_->next_ = task.next_;
312*61c4878aSAndroid Build Coastguard Worker }
313*61c4878aSAndroid Build Coastguard Worker if (task.next_ != nullptr) {
314*61c4878aSAndroid Build Coastguard Worker task.next_->prev_ = task.prev_;
315*61c4878aSAndroid Build Coastguard Worker }
316*61c4878aSAndroid Build Coastguard Worker task.prev_ = nullptr;
317*61c4878aSAndroid Build Coastguard Worker task.next_ = nullptr;
318*61c4878aSAndroid Build Coastguard Worker }
319*61c4878aSAndroid Build Coastguard Worker
RemoveWokenTaskLocked(Task & task)320*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::RemoveWokenTaskLocked(Task& task) {
321*61c4878aSAndroid Build Coastguard Worker if (first_woken_ == &task) {
322*61c4878aSAndroid Build Coastguard Worker first_woken_ = task.next_;
323*61c4878aSAndroid Build Coastguard Worker }
324*61c4878aSAndroid Build Coastguard Worker if (last_woken_ == &task) {
325*61c4878aSAndroid Build Coastguard Worker last_woken_ = task.prev_;
326*61c4878aSAndroid Build Coastguard Worker }
327*61c4878aSAndroid Build Coastguard Worker RemoveTaskFromList(task);
328*61c4878aSAndroid Build Coastguard Worker }
329*61c4878aSAndroid Build Coastguard Worker
RemoveSleepingTaskLocked(Task & task)330*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::RemoveSleepingTaskLocked(Task& task) {
331*61c4878aSAndroid Build Coastguard Worker if (sleeping_ == &task) {
332*61c4878aSAndroid Build Coastguard Worker sleeping_ = task.next_;
333*61c4878aSAndroid Build Coastguard Worker }
334*61c4878aSAndroid Build Coastguard Worker RemoveTaskFromList(task);
335*61c4878aSAndroid Build Coastguard Worker }
336*61c4878aSAndroid Build Coastguard Worker
AddTaskToWokenList(Task & task)337*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::AddTaskToWokenList(Task& task) {
338*61c4878aSAndroid Build Coastguard Worker if (first_woken_ == nullptr) {
339*61c4878aSAndroid Build Coastguard Worker first_woken_ = &task;
340*61c4878aSAndroid Build Coastguard Worker } else {
341*61c4878aSAndroid Build Coastguard Worker last_woken_->next_ = &task;
342*61c4878aSAndroid Build Coastguard Worker task.prev_ = last_woken_;
343*61c4878aSAndroid Build Coastguard Worker }
344*61c4878aSAndroid Build Coastguard Worker last_woken_ = &task;
345*61c4878aSAndroid Build Coastguard Worker }
346*61c4878aSAndroid Build Coastguard Worker
AddTaskToSleepingList(Task & task)347*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::AddTaskToSleepingList(Task& task) {
348*61c4878aSAndroid Build Coastguard Worker if (sleeping_ != nullptr) {
349*61c4878aSAndroid Build Coastguard Worker sleeping_->prev_ = &task;
350*61c4878aSAndroid Build Coastguard Worker }
351*61c4878aSAndroid Build Coastguard Worker task.next_ = sleeping_;
352*61c4878aSAndroid Build Coastguard Worker sleeping_ = &task;
353*61c4878aSAndroid Build Coastguard Worker }
354*61c4878aSAndroid Build Coastguard Worker
WakeTask(Task & task)355*61c4878aSAndroid Build Coastguard Worker void NativeDispatcherBase::WakeTask(Task& task) {
356*61c4878aSAndroid Build Coastguard Worker switch (task.state_) {
357*61c4878aSAndroid Build Coastguard Worker case Task::State::kWoken:
358*61c4878aSAndroid Build Coastguard Worker // Do nothing-- this has already been woken.
359*61c4878aSAndroid Build Coastguard Worker return;
360*61c4878aSAndroid Build Coastguard Worker case Task::State::kUnposted:
361*61c4878aSAndroid Build Coastguard Worker // This should be unreachable.
362*61c4878aSAndroid Build Coastguard Worker PW_CHECK(false);
363*61c4878aSAndroid Build Coastguard Worker case Task::State::kRunning:
364*61c4878aSAndroid Build Coastguard Worker // Wake again to indicate that this task should be run once more,
365*61c4878aSAndroid Build Coastguard Worker // as the state of the world may have changed since the task
366*61c4878aSAndroid Build Coastguard Worker // started running.
367*61c4878aSAndroid Build Coastguard Worker break;
368*61c4878aSAndroid Build Coastguard Worker case Task::State::kSleeping:
369*61c4878aSAndroid Build Coastguard Worker RemoveSleepingTaskLocked(task);
370*61c4878aSAndroid Build Coastguard Worker // Wake away!
371*61c4878aSAndroid Build Coastguard Worker break;
372*61c4878aSAndroid Build Coastguard Worker }
373*61c4878aSAndroid Build Coastguard Worker task.state_ = Task::State::kWoken;
374*61c4878aSAndroid Build Coastguard Worker AddTaskToWokenList(task);
375*61c4878aSAndroid Build Coastguard Worker if (wants_wake_) {
376*61c4878aSAndroid Build Coastguard Worker // Note: it's quite annoying to make this call under the lock, as it can
377*61c4878aSAndroid Build Coastguard Worker // result in extra thread wakeup/sleep cycles.
378*61c4878aSAndroid Build Coastguard Worker //
379*61c4878aSAndroid Build Coastguard Worker // However, releasing the lock first would allow for the possibility that
380*61c4878aSAndroid Build Coastguard Worker // the ``Dispatcher`` has been destroyed, making the call invalid.
381*61c4878aSAndroid Build Coastguard Worker DoWake();
382*61c4878aSAndroid Build Coastguard Worker }
383*61c4878aSAndroid Build Coastguard Worker }
384*61c4878aSAndroid Build Coastguard Worker
PopWokenTask()385*61c4878aSAndroid Build Coastguard Worker Task* NativeDispatcherBase::PopWokenTask() {
386*61c4878aSAndroid Build Coastguard Worker if (first_woken_ == nullptr) {
387*61c4878aSAndroid Build Coastguard Worker return nullptr;
388*61c4878aSAndroid Build Coastguard Worker }
389*61c4878aSAndroid Build Coastguard Worker Task& task = *first_woken_;
390*61c4878aSAndroid Build Coastguard Worker if (task.next_ != nullptr) {
391*61c4878aSAndroid Build Coastguard Worker task.next_->prev_ = nullptr;
392*61c4878aSAndroid Build Coastguard Worker } else {
393*61c4878aSAndroid Build Coastguard Worker last_woken_ = nullptr;
394*61c4878aSAndroid Build Coastguard Worker }
395*61c4878aSAndroid Build Coastguard Worker first_woken_ = task.next_;
396*61c4878aSAndroid Build Coastguard Worker task.prev_ = nullptr;
397*61c4878aSAndroid Build Coastguard Worker task.next_ = nullptr;
398*61c4878aSAndroid Build Coastguard Worker return &task;
399*61c4878aSAndroid Build Coastguard Worker }
400*61c4878aSAndroid Build Coastguard Worker
401*61c4878aSAndroid Build Coastguard Worker } // namespace pw::async2
402