Pigweed
 
Loading...
Searching...
No Matches
dispatcher_base.h
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
22namespace pw::async2 {
23
33inline pw::sync::InterruptSpinLock& dispatcher_lock() {
34 static NoDestructor<pw::sync::InterruptSpinLock> lock;
35 return *lock;
36}
37
38class NativeDispatcherBase;
39class Waker;
40
41// Forward-declare ``Dispatcher``.
42// This concrete type must be provided by a backend.
43class Dispatcher;
44
52class Context {
53 public:
57 : dispatcher_(&dispatcher), waker_(&waker) {}
58
63 Dispatcher& dispatcher() { return *dispatcher_; }
64
79 void ReEnqueue();
80
85 void InternalStoreWaker(Waker& waker_out);
86
87 private:
88 Dispatcher* dispatcher_;
89 Waker* waker_;
90};
91
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
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
118template <typename T>
119using PendOutputOf = typename decltype(std::declval<T>().Pend(
120 std::declval<Context&>()))::OutputType;
121
122template <typename, typename = void>
123constexpr bool is_pendable = false;
124
125template <typename T>
126constexpr bool is_pendable<T, std::void_t<PendOutputOf<T>>> = true;
127
158class Task {
159 friend class Dispatcher;
160 friend class Waker;
161 friend class NativeDispatcherBase;
162
163 public:
164 Task() = default;
165 Task(const Task&) = delete;
166 Task(Task&&) = delete;
167 Task& operator=(const Task&) = delete;
168 Task& operator=(Task&&) = delete;
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
193 Poll<> Pend(Context& cx) { return DoPend(cx); }
194
203 bool IsRegistered() const;
204
220
227 void Destroy() { DoDestroy(); }
228
229 private:
234 bool TryDeregister() PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock());
235
253 virtual Poll<> DoPend(Context&) = 0;
254
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
318class 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
329 Waker& operator=(Waker&& other) noexcept PW_LOCKS_EXCLUDED(dispatcher_lock());
330
331 ~Waker() noexcept { RemoveFromTaskWakerList(); }
332
342 void Wake() && PW_LOCKS_EXCLUDED(dispatcher_lock());
343
352 void InternalCloneInto(Waker& waker_out) &
353 PW_LOCKS_EXCLUDED(dispatcher_lock());
354
363 [[nodiscard]] bool IsEmpty() const PW_LOCKS_EXCLUDED(dispatcher_lock());
364
371 void Clear() PW_LOCKS_EXCLUDED(dispatcher_lock()) {
372 RemoveFromTaskWakerList();
373 }
374
375 private:
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.
397PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wnon-virtual-dtor");
398
407 public:
408 NativeDispatcherBase() = default;
413
414 protected:
415 ~NativeDispatcherBase() = default;
416
418 bool HasPostedTask(Task& task)
419 PW_EXCLUSIVE_LOCKS_REQUIRED(dispatcher_lock()) {
420 return task.dispatcher_ == this;
421 }
422
430 void Deregister() PW_LOCKS_EXCLUDED(dispatcher_lock());
431
432 void Post(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock());
433
438 class [[nodiscard]] SleepInfo {
439 friend class NativeDispatcherBase;
440
441 public:
442 bool should_sleep() const { return should_sleep_; }
443
444 private:
445 SleepInfo(bool should_sleep) : should_sleep_(should_sleep) {}
446
447 static SleepInfo DontSleep() { return SleepInfo(false); }
448
449 static SleepInfo Indefinitely() { return SleepInfo(true); }
450
451 bool should_sleep_;
452 };
453
468 PW_LOCKS_EXCLUDED(dispatcher_lock());
469
473 class [[nodiscard]] RunOneTaskResult {
474 public:
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) {}
481 bool completed_all_tasks() const { return completed_all_tasks_; }
482 bool completed_main_task() const { return completed_main_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
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
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
554
555} // namespace pw::async2
Definition: dispatcher_base.h:52
void InternalStoreWaker(Waker &waker_out)
Context(Dispatcher &dispatcher, Waker &waker)
Definition: dispatcher_base.h:56
Dispatcher & dispatcher()
Definition: dispatcher_base.h:63
A single-threaded cooperatively-scheduled runtime for async tasks.
Definition: dispatcher.h:42
Definition: dispatcher_base.h:438
Definition: dispatcher_base.h:406
RunOneTaskResult RunOneTask(Dispatcher &dispatcher, Task *task_to_look_for)
bool HasPostedTask(Task &task)
Check that a task is posted on this Dispatcher.
Definition: dispatcher_base.h:418
SleepInfo AttemptRequestWake(bool allow_empty)
Definition: poll.h:54
Definition: dispatcher_base.h:158
void Destroy()
Definition: dispatcher_base.h:227
virtual Poll DoPend(Context &)=0
Poll Pend(Context &cx)
Definition: dispatcher_base.h:193
bool IsRegistered() const
virtual void DoDestroy()
Definition: dispatcher_base.h:262
Definition: dispatcher_base.h:318
Waker & operator=(Waker &&other) noexcept
void Clear()
Definition: dispatcher_base.h:371
bool IsEmpty() const
void InternalCloneInto(Waker &waker_out) &
Definition: interrupt_spin_lock.h:48
#define PW_MODIFY_DIAGNOSTICS_POP()
Definition: compiler.h:191
#define PW_MODIFY_DIAGNOSTIC_GCC(kind, option)
Definition: compiler.h:208
#define PW_MODIFY_DIAGNOSTICS_PUSH()
Definition: compiler.h:186
Provides basic helpers for reading and writing UTF-8 encoded strings.
Definition: alignment.h:27