C/C++ API Reference
Loading...
Searching...
No Matches
waker.h
1// Copyright 2025 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
18#include "pw_assert/assert.h"
19#include "pw_async2/internal/config.h"
20#include "pw_async2/internal/lock.h"
21#include "pw_containers/intrusive_forward_list.h"
22#include "pw_log/tokenized_args.h"
23#include "pw_sync/lock_annotations.h"
24
25namespace pw::async2 {
26
27class Task;
28class Waker;
29
30namespace internal {
31
32template <typename Callable>
33[[nodiscard]] constexpr auto InvokeWithNodiscard(Callable&& callable) {
34 return callable();
35}
36
37[[nodiscard]] bool CloneWaker(Waker& waker_in,
38 Waker& waker_out,
39 log::Token wait_reason = log::kDefaultToken)
40 PW_LOCKS_EXCLUDED(internal::lock());
41
42} // namespace internal
43
45
58#define PW_ASYNC_STORE_WAKER(context, waker_out, wait_reason_string) \
59 do { \
60 bool waker_had_space = \
61 PW_ASYNC_TRY_STORE_WAKER(context, waker_out, wait_reason_string); \
62 PW_ASSERT(waker_had_space); \
63 } while (0)
64
78#define PW_ASYNC_TRY_STORE_WAKER(context, waker_out, wait_reason_string) \
79 _PW_ASYNC_TRY_GET_WAKER(::pw::async2::internal::StoreWaker, \
80 context, \
81 waker_out, \
82 wait_reason_string)
83
96#define PW_ASYNC_CLONE_WAKER(waker_in, waker_out, wait_reason_string) \
97 do { \
98 bool waker_had_space = \
99 PW_ASYNC_TRY_CLONE_WAKER(waker_in, waker_out, wait_reason_string); \
100 PW_ASSERT(waker_had_space); \
101 } while (0)
102
115#define PW_ASYNC_TRY_CLONE_WAKER(waker_in, waker_out, wait_reason_string) \
116 _PW_ASYNC_TRY_GET_WAKER(::pw::async2::internal::CloneWaker, \
117 waker_in, \
118 waker_out, \
119 wait_reason_string)
120
122
123// Base implementation of the TRY_{STORE,CLONE}_WAKER macros.
124#define _PW_ASYNC_TRY_GET_WAKER(func, source, waker_out, wait_reason_string) \
125 ::pw::async2::internal::InvokeWithNodiscard( \
126 [&]() PW_NO_LOCK_SAFETY_ANALYSIS { \
127 [[maybe_unused]] constexpr const char* \
128 pw_async2_wait_reason_must_be_string = wait_reason_string; \
129 constexpr ::pw::log::Token pw_async2_wait_reason = \
130 PW_LOG_TOKEN("pw_async2", wait_reason_string); \
131 return func(source, waker_out, pw_async2_wait_reason); \
132 })
133
135
149class Waker : public pw::IntrusiveForwardList<Waker>::Item {
150 friend class Task;
151 friend class Dispatcher;
152
153 public:
154 constexpr Waker() = default;
155 Waker(Waker&& other) noexcept PW_LOCKS_EXCLUDED(internal::lock()) {
156 *this = std::move(other);
157 }
158
162 Waker& operator=(Waker&& other) noexcept PW_LOCKS_EXCLUDED(internal::lock());
163
164 ~Waker() noexcept { Clear(); }
165
172 void Wake() PW_LOCKS_EXCLUDED(internal::lock());
173
182 [[nodiscard]] bool IsEmpty() const PW_LOCKS_EXCLUDED(internal::lock());
183
190 void Clear() PW_LOCKS_EXCLUDED(internal::lock());
191
192 private:
193 friend bool internal::CloneWaker(Waker& waker_in,
194 Waker& waker_out,
195 log::Token wait_reason);
196
197 Waker(Task& task) PW_LOCKS_EXCLUDED(internal::lock()) : task_(&task) {
198 std::lock_guard lock(internal::lock());
199 AddToTask();
200 }
201
202 // Creates a second `Waker` from this `Waker`.
203 //
204 // `Clone` is made explicit in order to allow for easier tracking of the
205 // different `Waker`s that may wake up a `Task`.
206 //
207 // This operation is guaranteed to be thread-safe.
208 void CloneInto(Waker& waker_out, log::Token wait_reason)
209 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
210
211 void AddToTask() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
212 void AddToTaskIfSet() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
213
214 void RemoveTask() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
215 void RemoveTaskIfSet() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
216
217 void ClearTask() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
218 task_ = nullptr;
219 }
220
221 // The `Task` to poll when awoken.
222 Task* task_ PW_GUARDED_BY(internal::lock()) = nullptr;
223
224#if PW_ASYNC2_DEBUG_WAIT_REASON
225 log::Token wait_reason_ = log::kDefaultToken;
226#endif // PW_ASYNC2_DEBUG_WAIT_REASON
227};
228
230
231} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: dispatcher.h:75
Definition: task.h:78
Definition: waker.h:149
Waker & operator=(Waker &&other) noexcept
bool IsEmpty() const
#define PW_GUARDED_BY(x)
Definition: lock_annotations.h:60
#define PW_EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: lock_annotations.h:147
#define PW_LOCKS_EXCLUDED(...)
Definition: lock_annotations.h:178