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