• 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 #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