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 Context;
28class Task;
29class Waker;
30
31namespace internal {
32
33template <typename Callable>
34[[nodiscard]] constexpr auto InvokeWithNodiscard(Callable&& callable) {
35 return callable();
36}
37
38[[nodiscard]] bool CloneWaker(Waker& waker_in,
39 Waker& waker_out,
40 log::Token wait_reason)
41 PW_LOCKS_EXCLUDED(internal::lock());
42
43[[nodiscard]] bool StoreWaker(Context& context,
44 Waker& waker_out,
45 log::Token wait_reason)
46 PW_LOCKS_EXCLUDED(internal::lock());
47
48} // namespace internal
49
51
64#define PW_ASYNC_STORE_WAKER(context, waker_out, wait_reason_string) \
65 do { \
66 bool waker_had_space = \
67 PW_ASYNC_TRY_STORE_WAKER(context, waker_out, wait_reason_string); \
68 PW_ASSERT(waker_had_space); \
69 } while (0)
70
84#define PW_ASYNC_TRY_STORE_WAKER(context, waker_out, wait_reason_string) \
85 _PW_ASYNC_TRY_GET_WAKER(::pw::async2::internal::StoreWaker, \
86 context, \
87 waker_out, \
88 wait_reason_string)
89
102#define PW_ASYNC_CLONE_WAKER(waker_in, waker_out, wait_reason_string) \
103 do { \
104 bool waker_had_space = \
105 PW_ASYNC_TRY_CLONE_WAKER(waker_in, waker_out, wait_reason_string); \
106 PW_ASSERT(waker_had_space); \
107 } while (0)
108
121#define PW_ASYNC_TRY_CLONE_WAKER(waker_in, waker_out, wait_reason_string) \
122 _PW_ASYNC_TRY_GET_WAKER(::pw::async2::internal::CloneWaker, \
123 waker_in, \
124 waker_out, \
125 wait_reason_string)
126
128
129// Base implementation of the TRY_{STORE,CLONE}_WAKER macros.
130#define _PW_ASYNC_TRY_GET_WAKER(func, source, waker_out, wait_reason_string) \
131 ::pw::async2::internal::InvokeWithNodiscard( \
132 [&]() PW_NO_LOCK_SAFETY_ANALYSIS { \
133 [[maybe_unused]] constexpr const char* \
134 pw_async2_wait_reason_must_be_string = wait_reason_string; \
135 constexpr ::pw::log::Token pw_async2_wait_reason = \
136 PW_LOG_TOKEN("pw_async2", wait_reason_string); \
137 return func(source, waker_out, pw_async2_wait_reason); \
138 })
139
141
155class Waker : public pw::IntrusiveForwardList<Waker>::Item {
156 public:
157 constexpr Waker() = default;
158
159 Waker(Waker&& other) noexcept PW_LOCKS_EXCLUDED(internal::lock()) {
160 *this = std::move(other);
161 }
162
166 Waker& operator=(Waker&& other) noexcept PW_LOCKS_EXCLUDED(internal::lock());
167
168 ~Waker() noexcept { Clear(); }
169
176 void Wake() PW_LOCKS_EXCLUDED(internal::lock());
177
186 [[nodiscard]] bool IsEmpty() const PW_LOCKS_EXCLUDED(internal::lock());
187
194 void Clear() PW_LOCKS_EXCLUDED(internal::lock());
195
196 private:
197 friend class Context;
198 friend class Task;
199 friend class Dispatcher;
200
201 friend bool internal::CloneWaker(Waker& waker_in,
202 Waker& waker_out,
203 log::Token wait_reason);
204
205 friend bool internal::StoreWaker(Context& context,
206 Waker& waker_out,
207 log::Token wait_reason);
208
209 Waker(Task& task, log::Token wait_reason) PW_LOCKS_EXCLUDED(internal::lock());
210
211 void set_wait_reason([[maybe_unused]] log::Token wait_reason)
212 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
213#if PW_ASYNC2_DEBUG_WAIT_REASON
214 wait_reason_ = wait_reason;
215#endif // PW_ASYNC2_DEBUG_WAIT_REASON
216 }
217
218 bool TrySetTask(Context& context, log::Token wait_reason)
219 PW_LOCKS_EXCLUDED(internal::lock());
220
221 // Creates a second `Waker` from this `Waker`.
222 //
223 // `Clone` is made explicit in order to allow for easier tracking of the
224 // different `Waker`s that may wake up a `Task`.
225 //
226 // This operation is guaranteed to be thread-safe.
227 bool CloneInto(Waker& waker_out, log::Token wait_reason)
228 PW_LOCKS_EXCLUDED(internal::lock());
229
230 void RemoveTask() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
231 void RemoveTaskIfSet() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
232 if (task_ != nullptr) {
233 RemoveTask();
234 }
235 }
236
237 void ClearTask() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
238 task_ = nullptr;
239 }
240
241 // The `Task` to poll when awoken.
242 Task* task_ PW_GUARDED_BY(internal::lock()) = nullptr;
243
244#if PW_ASYNC2_DEBUG_WAIT_REASON
245 log::Token wait_reason_ PW_GUARDED_BY(internal::lock()) = log::kDefaultToken;
246#endif // PW_ASYNC2_DEBUG_WAIT_REASON
247};
248
250
251namespace internal {
252
253inline bool CloneWaker(Waker& waker_in,
254 Waker& waker_out,
255 log::Token wait_reason) {
256 return waker_in.CloneInto(waker_out, wait_reason);
257}
258
259inline bool StoreWaker(Context& context,
260 Waker& waker_out,
261 log::Token wait_reason) {
262 return waker_out.TrySetTask(context, wait_reason);
263}
264
265} // namespace internal
266} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: task.h:45
Definition: dispatcher.h:74
Definition: task.h:123
Definition: waker.h:155
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