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 #pragma once
15
16 #include <mutex>
17 #include <optional>
18
19 #include "pw_assert/assert.h"
20 #include "pw_async2/poll.h"
21 #include "pw_chrono/system_clock.h"
22 #include "pw_sync/interrupt_spin_lock.h"
23 #include "pw_sync/lock_annotations.h"
24 #include "pw_toolchain/no_destructor.h"
25
26 namespace pw::async2 {
27
28 /// A lock guarding the ``Task`` queue and ``Waker`` lists.
29 ///
30 /// This is an ``InterruptSpinLock`` in order to allow posting work from ISR
31 /// contexts.
32 ///
33 /// This lock is global rather than per-dispatcher in order to allow ``Task``
34 /// and ``Waker`` to take out the lock without dereferencing their
35 /// ``Dispatcher*`` fields, which are themselves guarded by the lock in order
36 /// to allow the ``Dispatcher`` to ``Deregister`` itself upon destruction.
dispatcher_lock()37 inline pw::sync::InterruptSpinLock& dispatcher_lock() {
38 static NoDestructor<pw::sync::InterruptSpinLock> lock;
39 return *lock;
40 }
41
42 class DispatcherBase;
43 class Waker;
44 class WaitReason;
45
46 // Forward-declare ``Dispatcher``.
47 // This concrete type must be provided by a backend.
48 class Dispatcher;
49
50 /// Context for an asynchronous ``Task``.
51 ///
52 /// This object contains resources needed for scheduling asynchronous work,
53 /// such as the current ``Dispatcher`` and the ``Waker`` for the current task.
54 ///
55 /// ``Context`` s are most often created by ``Dispatcher`` s, which pass them
56 /// into ``Task::Pend``.
57 class Context {
58 public:
59 /// Creates a new ``Context`` containing the currently-running ``Dispatcher``
60 /// and a ``Waker`` for the current ``Task``.
Context(Dispatcher & dispatcher,Waker & waker)61 Context(Dispatcher& dispatcher, Waker& waker)
62 : dispatcher_(&dispatcher), waker_(&waker) {}
63
64 /// The ``Dispatcher`` on which the current ``Task`` is executing.
65 ///
66 /// This can be used for spawning new tasks using
67 /// ``dispatcher().Post(task);``.
dispatcher()68 Dispatcher& dispatcher() { return *dispatcher_; }
69
70 /// Queues the current ``Task::Pend`` to run again in the future, possibly
71 /// after other work is performed.
72 ///
73 /// This may be used by ``Task`` implementations that wish to provide
74 /// additional fairness by yielding to the dispatch loop rather than perform
75 /// too much work in a single iteration.
76 ///
77 /// This is semantically equivalent to calling ``GetWaker(...).Wake()``
78 void ReEnqueue();
79
80 /// Returns a ``Waker`` which, when awoken, will cause the current task to be
81 /// ``Pend``'d by its dispatcher.
82 Waker GetWaker(WaitReason reason);
83
84 private:
85 Dispatcher* dispatcher_;
86 Waker* waker_;
87 };
88
89 /// A task which may complete one or more asynchronous operations.
90 ///
91 /// The ``Task`` interface is commonly implemented by users wishing to schedule
92 /// work on an asynchronous ``Dispatcher``. To do this, users may subclass
93 /// ``Task``, providing an implementation of the ``DoPend`` method which
94 /// advances the state of the ``Task`` as far as possible before yielding back
95 /// to the ``Dispatcher``.
96 ///
97 /// This process works similarly to cooperatively-scheduled green threads or
98 /// coroutines, with a ``Task`` representing a single logical "thread" of
99 /// execution. Unlike some green thread or coroutine implementations, ``Task``
100 /// does not imply a separately-allocated stack: ``Task`` state is most
101 /// commonly stored in fields of the ``Task`` subclass.
102 ///
103 /// Once defined by a user, ``Task`` s may be run by passing them to a
104 /// ``Dispatcher`` via ``Dispatcher::Post``. The ``Dispatcher`` will then
105 /// ``Pend`` the ``Task`` every time that the ``Task`` indicates it is able
106 /// to make progress.
107 ///
108 /// Note that ``Task`` objects *must not* be destroyed while they are actively
109 /// being ``Pend``'d by a ``Dispatcher``. The best way to ensure this is to
110 /// create ``Task`` objects that continue to live until they receive a
111 /// ``DoDestroy`` call or which outlive their associated ``Dispatcher``.
112 class Task {
113 friend class Waker;
114 friend class DispatcherBase;
115 template <typename T>
116 friend class DispatcherImpl;
117
118 public:
119 Task() = default;
120 Task(Task&) = delete;
121 Task(Task&&) = delete;
122 Task& operator=(Task&) = delete;
123 Task& operator=(Task&&) = delete;
~Task()124 virtual ~Task() {
125 // Note: the task must not be registered with a ``Dispatcher` upon
126 // destruction. This happens automatically upon ``Task`` completion or upon
127 // ``Dispatcher`` destruction.
128 //
129 // This is necessary to ensure that neither the ``Dispatcher`` nor
130 // ``Waker`` reference the ``Task`` object after destruction.
131 //
132 // Note that the ``~Task`` destructor cannot perform this deregistration,
133 // as there is no guarantee that (1) the task is not being actively polled
134 // and (2) by the time the ``~Task`` destructor is reached, the subclass
135 // destructor has already run, invalidating the subclass state that may be
136 // read by the ``Pend`` implementation.
137 }
138
139 // A public interface for ``DoPend``.
140 //
141 // ``DoPend`` is normally invoked by a ``Dispatcher`` after a ``Task`` has
142 // been ``Post`` ed.
143 //
144 // This wrapper should only be called by ``Task`` s delegating to other
145 // ``Task`` s. For example, a ``class MainTask`` might have separate fields
146 // for ``TaskA` and ``TaskB``, and could invoke ``Pend`` on these types
147 // within its own ``DoPend`` implementation.
Pend(Context & cx)148 Poll<> Pend(Context& cx) { return DoPend(cx); }
149
150 // A public interface for ``DoDestroy``.
151 //
152 // ``DoDestroy`` is normally invoked by a ``Dispatcher`` after a ``Post`` ed
153 // ``Task`` has completed.
154 //
155 // This should only be called by ``Task`` s delegating to other ``Task`` s.
Destroy()156 void Destroy() { DoDestroy(); }
157
158 private:
159 /// Attempts to advance this ``Task`` to completion.
160 ///
161 /// This method should not perform synchronous waiting, as doing so may block
162 /// the main ``Dispatcher`` loop and prevent other ``Task`` s from
163 /// progressing. Because of this, ``Task`` s should not invoke blocking
164 /// ``Dispatcher`` methods such as ``RunUntilComplete``.
165 ///
166 /// ``Task`` s should also avoid invoking ``RunUntilStalled` on their own
167 /// ``Dispatcher``.
168 ///
169 /// Returns ``Ready`` if complete, or ``Pending`` if the ``Task`` was not yet
170 /// able to complete.
171 ///
172 /// If ``Pending`` is returned, the ``Task`` must ensure it is woken up when
173 /// it is able to make progress. To do this, ``Task::Pend`` must arrange for
174 /// ``Waker::Wake`` to be called, either by storing a copy of the ``Waker``
175 /// away to be awoken by another system (such as an interrupt handler).
176 virtual Poll<> DoPend(Context&) = 0;
177
178 /// Performs any necessary cleanup of ``Task`` memory after completion.
179 ///
180 /// This may include calls to ``std::destroy_at(this)``, and may involve
181 /// deallocating the memory for this ``Task`` itself.
182 ///
183 /// Tasks implementations which wish to be reused may skip self-destruction
184 /// here.
DoDestroy()185 virtual void DoDestroy() {}
186
187 // Unlinks all ``Waker`` objects associated with this ``Task.``
188 void RemoveAllWakersLocked() PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
189
190 // Adds a ``Waker`` to the linked list of ``Waker`` s tracked by this
191 // ``Task``.
192 void AddWakerLocked(Waker&) PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
193
194 // Removes a ``Waker`` from the linked list of ``Waker`` s tracked by this
195 // ``Task``
196 //
197 // Precondition: the provided waker *must* be in the list of ``Waker`` s
198 // tracked by this ``Task``.
199 void RemoveWakerLocked(Waker&) PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
200
201 enum class State {
202 kUnposted,
203 kRunning,
204 kWoken,
205 kSleeping,
206 };
207 // The current state of the task.
208 State state_ PW_GUARDED_BY(dispatcher_lock()) = State::kUnposted;
209
210 // A pointer to the dispatcher this task is associated with.
211 //
212 // This will be non-null when `state_` is anything other than `kUnposted`.
213 //
214 // This value must be cleared by the dispatcher upon destruction in order to
215 // prevent null access.
216 DispatcherBase* dispatcher_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
217
218 // Pointers for whatever linked-list this ``Task`` is in.
219 // These are controlled by the ``Dispatcher``.
220 Task* prev_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
221 Task* next_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
222
223 // A pointer to the first element of the linked list of ``Waker`` s that may
224 // awaken this ``Task``.
225 Waker* wakers_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
226 };
227
228 /// An identifier indicating the kind of event a ``Waker`` is waiting for.
229 ///
230 /// This identifier may be stored for debugging purposes.
231 class WaitReason {
232 public:
233 /// Indicates that the wait is happen for an unspecified reason.
Unspecified()234 static WaitReason Unspecified() { return WaitReason(); }
235
236 private:
WaitReason()237 WaitReason() {}
238 };
239
240 /// An object which can respond to asynchronous events by queueing work to
241 /// be done in response, such as placing a ``Task`` on a ``Dispatcher`` loop.
242 ///
243 /// ``Waker`` s are often held by I/O objects, custom concurrency primitives,
244 /// or interrupt handlers. Once the thing the ``Task`` was waiting for is
245 /// available, ``Wake`` should be called so that the ``Task`` is alerted and
246 /// may process the event.
247 ///
248 /// ``Waker`` s may be held for any lifetime, and will be automatically
249 /// nullified when the underlying ``Dispatcher`` or ``Task`` is deleted.
250 ///
251 /// ``Waker`` s are most commonly created by ``Dispatcher`` s, which pass them
252 /// into ``Task::Pend`` via its ``Context`` argument.
253 class Waker {
254 friend class Task;
255 friend class DispatcherBase;
256 template <typename T>
257 friend class DispatcherImpl;
258
259 public:
260 Waker() = default;
261 Waker(Waker&& other) noexcept PW_LOCKS_EXCLUDED(dispatcher_lock());
262
263 /// Replace this ``Waker`` with another.
264 ///
265 /// This operation is guaranteed to be thread-safe.
266 Waker& operator=(Waker&& other) noexcept PW_LOCKS_EXCLUDED(dispatcher_lock());
267
~Waker()268 ~Waker() noexcept { RemoveFromTaskWakerList(); }
269
270 /// Wakes up the ``Waker``'s creator, alerting it that an asynchronous
271 /// event has occurred that may allow it to make progress.
272 ///
273 /// ``Wake`` operates on an rvalue reference (``&&``) in order to indicate
274 /// that the event that was waited on has been complete. This makes it
275 /// possible to track the outstanding events that may cause a ``Task`` to
276 /// wake up and make progress.
277 ///
278 /// This operation is guaranteed to be thread-safe.
279 void Wake() && PW_LOCKS_EXCLUDED(dispatcher_lock());
280
281 /// Creates a second ``Waker`` from this ``Waker``.
282 ///
283 /// ``Clone`` is made explicit in order to allow for easier tracking of
284 /// the different ``Waker``s that may wake up a ``Task``.
285 ///
286 /// The ``WaitReason`` argument can be used to provide information about
287 /// what event the ``Waker`` is waiting on. This can be useful for
288 /// debugging purposes.
289 ///
290 /// This operation is guaranteed to be thread-safe.
291 Waker Clone(WaitReason reason) & PW_LOCKS_EXCLUDED(dispatcher_lock());
292
293 /// Returns whether this ``Waker`` is empty.
294 ///
295 /// Empty wakers are those that perform no action upon wake. These may be
296 /// created either via the default no-argument constructor or by
297 /// calling ``Clear`` or ``std::move`` on a ``Waker``, after which the
298 /// moved-from ``Waker`` will be empty.
299 ///
300 /// This operation is guaranteed to be thread-safe.
301 [[nodiscard]] bool IsEmpty() const PW_LOCKS_EXCLUDED(dispatcher_lock());
302
303 /// Clears this ``Waker``.
304 ///
305 /// After this call, ``Wake`` will no longer perform any action, and
306 /// ``IsEmpty`` will return ``true``.
307 ///
308 /// This operation is guaranteed to be thread-safe.
Clear()309 void Clear() PW_LOCKS_EXCLUDED(dispatcher_lock()) {
310 RemoveFromTaskWakerList();
311 }
312
313 private:
Waker(Task & task)314 Waker(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) : task_(&task) {
315 InsertIntoTaskWakerList();
316 }
317
318 void InsertIntoTaskWakerList();
319 void InsertIntoTaskWakerListLocked()
320 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
321 void RemoveFromTaskWakerList();
322 void RemoveFromTaskWakerListLocked()
323 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
324
325 // The ``Task`` to poll when awoken.
326 Task* task_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
327
328 // The next ``Waker`` that may awaken this ``Task``.
329 // This list is controlled by the corresponding ``Task``.
330 Waker* next_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
331 };
332
333 /// A base class used by ``Dispatcher`` implementations.
334 ///
335 /// Note that only one ``Dispatcher`` implementation should exist per
336 /// toolchain. However, a common base class is used in order to share
337 /// behavior and standardize the interface of these ``Dispatcher`` s,
338 /// and to prevent build system cycles due to ``Task`` needing to refer
339 /// to the ``Dispatcher`` class.
340 class DispatcherBase {
341 public:
342 DispatcherBase() = default;
343 DispatcherBase(DispatcherBase&) = delete;
344 DispatcherBase(DispatcherBase&&) = delete;
345 DispatcherBase& operator=(DispatcherBase&) = delete;
346 DispatcherBase& operator=(DispatcherBase&&) = delete;
~DispatcherBase()347 virtual ~DispatcherBase() {}
348
349 protected:
350 /// Check that a task is posted on this ``Dispatcher``.
HasPostedTask(Task & task)351 bool HasPostedTask(Task& task)
352 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock()) {
353 return task.dispatcher_ == this;
354 }
355
356 /// Removes references to this ``DispatcherBase`` from all linked ``Task`` s
357 /// and ``Waker`` s.
358 ///
359 /// This must be called by ``Dispatcher`` implementations in their
360 /// destructors. It is not called by the ``DispatcherBase`` destructor, as
361 /// doing so would allow the ``Dispatcher`` to be referenced between the
362 /// calls to ``~Dispatcher`` and ``~DispatcherBase``.
363 void Deregister() PW_LOCKS_EXCLUDED(dispatcher_lock());
364
365 private:
366 friend class Task;
367 friend class Waker;
368 template <typename Impl>
369 friend class DispatcherImpl;
370
371 /// Sends a wakeup signal to this ``Dispatcher``.
372 ///
373 /// This method's implementation should ensure that the ``Dispatcher`` comes
374 /// back from sleep and begins invoking ``RunOneTask`` again.
375 ///
376 /// Note: the ``dispatcher_lock()`` may or may not be held here, so it must
377 /// not be acquired by ``DoWake``, nor may ``DoWake`` assume that it has been
378 /// acquired.
379 virtual void DoWake() = 0;
380
381 static void UnpostTaskList(Task*)
382 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
383 static void RemoveTaskFromList(Task&)
384 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
385 void RemoveWokenTaskLocked(Task&)
386 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
387 void RemoveSleepingTaskLocked(Task&)
388 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
389
390 // For use by ``WakeTask`` and ``DispatcherImpl::Post``.
391 void AddTaskToWokenList(Task&) PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
392
393 // For use by ``RunOneTask``.
394 void AddTaskToSleepingList(Task&)
395 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
396
397 // For use by ``Waker``.
398 void WakeTask(Task&) PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
399
400 // For use by ``RunOneTask``.
401 Task* PopWokenTask() PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
402
403 Task* first_woken_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
404 Task* last_woken_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
405 // Note: the sleeping list's order is not significant.
406 Task* sleeping_ PW_GUARDED_BY(dispatcher_lock()) = nullptr;
407 bool wants_wake_ PW_GUARDED_BY(dispatcher_lock()) = false;
408 };
409
410 /// Information about whether and when to sleep until as returned by
411 /// ``DispatcherBase::AttemptRequestWake``.
412 ///
413 /// This should only be used by ``Dispatcher`` implementations.
414 class [[nodiscard]] SleepInfo {
415 template <typename T>
416 friend class DispatcherImpl;
417
418 public:
should_sleep()419 bool should_sleep() const { return should_sleep_; }
420
421 private:
SleepInfo(bool should_sleep)422 SleepInfo(bool should_sleep) : should_sleep_(should_sleep) {}
423
DontSleep()424 static SleepInfo DontSleep() { return SleepInfo(false); }
425
Indefinitely()426 static SleepInfo Indefinitely() { return SleepInfo(true); }
427
428 bool should_sleep_;
429 };
430
431 /// Information about the result of a call to ``RunOneTask``.
432 ///
433 /// This should only be used by ``Dispatcher`` implementations.
434 class [[nodiscard]] RunOneTaskResult {
435 template <typename T>
436 friend class DispatcherImpl;
437
438 public:
RunOneTaskResult(bool completed_all_tasks,bool completed_main_task,bool ran_a_task)439 RunOneTaskResult(bool completed_all_tasks,
440 bool completed_main_task,
441 bool ran_a_task)
442 : completed_all_tasks_(completed_all_tasks),
443 completed_main_task_(completed_main_task),
444 ran_a_task_(ran_a_task) {}
completed_all_tasks()445 bool completed_all_tasks() const { return completed_all_tasks_; }
completed_main_task()446 bool completed_main_task() const { return completed_main_task_; }
ran_a_task()447 bool ran_a_task() const { return ran_a_task_; }
448
449 private:
450 bool completed_all_tasks_;
451 bool completed_main_task_;
452 bool ran_a_task_;
453 };
454
455 /// A CRTP base class used by ``Dispatcher`` implementations.
456 ///
457 /// This is used to provide a common public interface for all ``Dispatcher``
458 /// implementations.
459 ///
460 /// Note that only one ``Dispatcher`` implementation should exist per
461 /// toolchain. However, a common base class is used in order to share
462 /// behavior and standardize the interface of these ``Dispatcher`` s,
463 /// and to prevent build system cycles due to ``Task`` needing to refer
464 /// to the ``Dispatcher`` class.
465 template <typename Impl>
466 class DispatcherImpl : public DispatcherBase {
467 public:
468 /// Tells the ``Dispatcher`` to run ``Task`` to completion.
469 /// This method does not block.
470 ///
471 /// After ``Post`` is called, ``Task::Pend`` will be invoked once.
472 /// If ``Task::Pend`` does not complete, the ``Dispatcher`` will wait
473 /// until the ``Task`` is "awoken", at which point it will call ``Pend``
474 /// again until the ``Task`` completes.
Post(Task & task)475 void Post(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) {
476 bool wake_dispatcher = false;
477 {
478 std::lock_guard lock(dispatcher_lock());
479 PW_DASSERT(task.state_ == Task::State::kUnposted);
480 PW_DASSERT(task.dispatcher_ == nullptr);
481 task.state_ = Task::State::kWoken;
482 task.dispatcher_ = this;
483 AddTaskToWokenList(task);
484 if (wants_wake_) {
485 wake_dispatcher = true;
486 wants_wake_ = false;
487 }
488 }
489 // Note: unlike in ``WakeTask``, here we know that the ``Dispatcher`` will
490 // not be destroyed out from under our feet because we're in a method being
491 // called on the ``Dispatcher`` by a user.
492 if (wake_dispatcher) {
493 DoWake();
494 }
495 }
496
497 /// Runs tasks until none are able to make immediate progress.
RunUntilStalled()498 Poll<> RunUntilStalled() PW_LOCKS_EXCLUDED(dispatcher_lock()) {
499 return self().DoRunUntilStalled(nullptr);
500 }
501
502 /// Runs tasks until none are able to make immediate progress, or until
503 /// ``task`` completes.
504 ///
505 /// Returns whether ``task`` completed.
RunUntilStalled(Task & task)506 Poll<> RunUntilStalled(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) {
507 return self().DoRunUntilStalled(&task);
508 }
509
510 /// Runs until all tasks complete.
RunToCompletion()511 void RunToCompletion() PW_LOCKS_EXCLUDED(dispatcher_lock()) {
512 self().DoRunToCompletion(nullptr);
513 }
514
515 /// Runs until ``task`` completes.
RunToCompletion(Task & task)516 void RunToCompletion(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) {
517 self().DoRunToCompletion(&task);
518 }
519
520 protected:
521 /// Indicates that this ``Dispatcher`` is about to go to sleep and
522 /// requests that it be awoken when more work is available in the future.
523 ///
524 /// Dispatchers must invoke this method before sleeping in order to ensure
525 /// that they receive a ``DoWake`` call when there is more work to do.
526 ///
527 /// The returned ``SleepInfo`` will describe whether and for how long the
528 /// ``Dispatcher`` implementation should go to sleep. Notably it will return
529 /// that the ``Dispatcher`` should not sleep if there is still more work to
530 /// be done.
AttemptRequestWake()531 SleepInfo AttemptRequestWake() PW_LOCKS_EXCLUDED(dispatcher_lock()) {
532 std::lock_guard lock(dispatcher_lock());
533 // Don't allow sleeping if there are already tasks waiting to be run.
534 if (first_woken_ != nullptr) {
535 return SleepInfo::DontSleep();
536 }
537 /// Indicate that the ``Dispatcher`` is sleeping and will need a ``DoWake``
538 /// call once more work can be done.
539 wants_wake_ = true;
540 // Once timers are added, this should check them.
541 return SleepInfo::Indefinitely();
542 }
543
544 /// Attempts to run a single task, returning whether any tasks were
545 /// run, and whether `task_to_look_for` was run.
RunOneTask(Task * task_to_look_for)546 [[nodiscard]] RunOneTaskResult RunOneTask(Task* task_to_look_for)
547 PW_LOCKS_EXCLUDED(dispatcher_lock()) {
548 Task* task;
549 {
550 std::lock_guard lock(dispatcher_lock());
551 task = PopWokenTask();
552 if (task == nullptr) {
553 bool all_complete = first_woken_ == nullptr && sleeping_ == nullptr;
554 return RunOneTaskResult(
555 /*completed_all_tasks=*/all_complete,
556 /*completed_main_task=*/false,
557 /*ran_a_task=*/false);
558 }
559 task->state_ = Task::State::kRunning;
560 }
561
562 bool complete;
563 {
564 Waker waker(*task);
565 Context context(self(), waker);
566 complete = task->Pend(context).IsReady();
567 }
568 if (complete) {
569 bool all_complete;
570 {
571 std::lock_guard lock(dispatcher_lock());
572 switch (task->state_) {
573 case Task::State::kUnposted:
574 case Task::State::kSleeping:
575 PW_DASSERT(false);
576 PW_UNREACHABLE;
577 case Task::State::kRunning:
578 break;
579 case Task::State::kWoken:
580 RemoveWokenTaskLocked(*task);
581 break;
582 }
583 task->state_ = Task::State::kUnposted;
584 task->dispatcher_ = nullptr;
585 task->RemoveAllWakersLocked();
586 all_complete = first_woken_ == nullptr && sleeping_ == nullptr;
587 }
588 task->DoDestroy();
589 return RunOneTaskResult(
590 /*completed_all_tasks=*/all_complete,
591 /*completed_main_task=*/task == task_to_look_for,
592 /*ran_a_task=*/true);
593 } else {
594 std::lock_guard lock(dispatcher_lock());
595 if (task->state_ == Task::State::kRunning) {
596 task->state_ = Task::State::kSleeping;
597 AddTaskToSleepingList(*task);
598 }
599 return RunOneTaskResult(
600 /*completed_all_tasks=*/false,
601 /*completed_main_task=*/false,
602 /*ran_a_task=*/true);
603 }
604 }
605
606 private:
607 /// Returns ``this`` as a base class reference.
self()608 Impl& self() { return *static_cast<Impl*>(this); }
609 };
610
611 } // namespace pw::async2
612