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