C/C++ API Reference
Loading...
Searching...
No Matches
once_sender.h
1// Copyright 2024 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/dispatcher.h"
17#include "pw_function/function.h"
18#include "pw_sync/mutex.h"
19
20namespace pw::async2 {
21
23
24// A lock guarding OnceReceiver and OnceSender member variables.
25//
26// This is an ``InterruptSpinLock`` in order to allow sending values from an
27// ISR context.
28inline pw::sync::InterruptSpinLock& sender_receiver_lock() {
29 static NoDestructor<pw::sync::InterruptSpinLock> lock;
30 return *lock;
31}
32
33template <typename T>
34class OnceSender;
35
41template <typename T>
42class OnceReceiver final {
43 public:
44 OnceReceiver() = default;
45
48 template <typename... Args>
49 explicit OnceReceiver(Args&&... value_args)
50 : value_(std::forward<Args>(value_args)...) {}
51
52 OnceReceiver(OnceReceiver&& other) {
53 std::lock_guard lock(sender_receiver_lock());
54 sender_ = other.sender_;
55 other.sender_ = nullptr;
56 if (sender_) {
57 sender_->receiver_ = this;
58 }
59 if (other.value_.has_value()) {
60 value_.emplace(std::move(other.value_.value()));
61 }
62 other.value_.reset();
63 waker_ = std::move(other.waker_);
64 }
65
66 OnceReceiver(const OnceReceiver&) = delete;
67 OnceReceiver& operator=(const OnceReceiver&) = delete;
68 OnceReceiver& operator=(OnceReceiver&& other) = delete;
69
70 ~OnceReceiver();
71
76 std::lock_guard lock(sender_receiver_lock());
77 if (value_.has_value()) {
78 return Ready(std::move(*value_));
79 } else if (!sender_) {
80 return Ready(Status::Cancelled());
81 }
83 cx,
84 waker_,
85 "OnceReceiver is waiting for a value to be sent into the "
86 "corresponding OnceSender");
87 return Pending();
88 }
89
90 private:
91 template <typename U>
92 friend std::pair<OnceSender<U>, OnceReceiver<U>> MakeOnceSenderAndReceiver();
93 template <typename U>
94 friend void InitializeOnceSenderAndReceiver(OnceSender<U>& sender,
95 OnceReceiver<U>& receiver);
96 friend class OnceSender<T>;
97
98 OnceSender<T>* sender_ PW_GUARDED_BY(sender_receiver_lock()) = nullptr;
99 std::optional<T> value_ PW_GUARDED_BY(sender_receiver_lock()) = std::nullopt;
100 Waker waker_;
101};
102
107template <typename T>
108class OnceSender final {
109 public:
110 OnceSender() = default;
111
112 ~OnceSender() {
113 std::lock_guard lock(sender_receiver_lock());
114 if (receiver_) {
115 std::move(receiver_->waker_).Wake();
116 receiver_->sender_ = nullptr;
117 }
118 }
119
120 OnceSender(OnceSender&& other) {
121 std::lock_guard lock(sender_receiver_lock());
122 receiver_ = other.receiver_;
123 other.receiver_ = nullptr;
124 if (receiver_) {
125 receiver_->sender_ = this;
126 }
127 }
128
129 OnceSender(const OnceSender&) = delete;
130 OnceSender& operator=(const OnceSender&) = delete;
131 OnceSender& operator=(OnceSender&& other) = delete;
132
134 template <typename... Args>
135 void emplace(Args&&... args) {
136 std::lock_guard lock(sender_receiver_lock());
137 if (receiver_) {
138 receiver_->value_.emplace(std::forward<Args>(args)...);
139 std::move(receiver_->waker_).Wake();
140 receiver_->sender_ = nullptr;
141 receiver_ = nullptr;
142 }
143 }
144
145 OnceSender& operator=(const T& value) {
146 emplace(value);
147 return *this;
148 }
149 OnceSender& operator=(T&& value) {
150 emplace(std::move(value));
151 return *this;
152 }
153
154 private:
155 template <typename U>
156 friend std::pair<OnceSender<U>, OnceReceiver<U>> MakeOnceSenderAndReceiver();
157 template <typename U>
158 friend void InitializeOnceSenderAndReceiver(OnceSender<U>& sender,
159 OnceReceiver<U>& receiver);
160 friend class OnceReceiver<T>;
161
162 OnceSender(OnceReceiver<T>* receiver) : receiver_(receiver) {}
163
164 OnceReceiver<T>* receiver_ PW_GUARDED_BY(sender_receiver_lock()) = nullptr;
165};
166
167template <typename T>
168OnceReceiver<T>::~OnceReceiver() {
169 std::lock_guard lock(sender_receiver_lock());
170 if (sender_) {
171 sender_->receiver_ = nullptr;
172 }
173}
174
176template <typename T>
177std::pair<OnceSender<T>, OnceReceiver<T>> MakeOnceSenderAndReceiver() {
178 std::pair<OnceSender<T>, OnceReceiver<T>> send_recv;
179 InitializeOnceSenderAndReceiver(send_recv.first, send_recv.second);
180 return send_recv;
181}
182
184template <typename T>
186 OnceReceiver<T>& receiver)
188 // Disable lock analysis because these are fresh sender/receiver pairs and
189 // do not require a lock to initialize;
190 receiver.sender_ = &sender;
191 sender.receiver_ = &receiver;
192}
193
194template <typename T>
195class OnceRefSender;
196
204template <typename T>
205class OnceRefReceiver final {
206 public:
207 OnceRefReceiver() = default;
208
210 std::lock_guard lock(sender_receiver_lock());
211 sender_ = other.sender_;
212 other.sender_ = nullptr;
213 if (sender_) {
214 sender_->receiver_ = this;
215 }
216 value_ = other.value_;
217 waker_ = std::move(other.waker_);
218 }
219
220 OnceRefReceiver(const OnceRefReceiver&) = delete;
221 OnceRefReceiver& operator=(const OnceRefReceiver&) = delete;
222 OnceRefReceiver& operator=(OnceRefReceiver&& other) = delete;
223
225
230 std::lock_guard lock(sender_receiver_lock());
231 if (value_ == nullptr) {
232 return Ready(OkStatus());
233 }
234 if (sender_ == nullptr) {
235 return Ready(Status::Cancelled());
236 }
238 cx,
239 waker_,
240 "OnceRefReceiver is waiting for OnceRefSender to write a value");
241 return Pending();
242 }
243
244 private:
245 template <typename U>
246 friend std::pair<OnceRefSender<U>, OnceRefReceiver<U>>
247 MakeOnceRefSenderAndReceiver(U&);
248 template <typename U>
249 friend void InitializeOnceRefSenderAndReceiver(OnceRefSender<U>& sender,
250 OnceRefReceiver<U>& receiver,
251 U& value);
252 friend class OnceRefSender<T>;
253
254 OnceRefReceiver(T& value) : value_(&value) {}
255
256 // Pointer to the value to be modified. Set to `nullptr` once modification
257 // is complete.
258 T* value_ PW_GUARDED_BY(sender_receiver_lock()) = nullptr;
259 // Pointer to the modifier. Set to `nullptr` if the sender disappears.
260 OnceRefSender<T>* sender_ PW_GUARDED_BY(sender_receiver_lock()) = nullptr;
261 Waker waker_;
262};
263
268template <typename T>
269class OnceRefSender final {
270 public:
271 OnceRefSender() = default;
272
274 std::lock_guard lock(sender_receiver_lock());
275 if (receiver_) {
276 receiver_->sender_ = nullptr;
277 std::move(receiver_->waker_).Wake();
278 }
279 }
280
282 std::lock_guard lock(sender_receiver_lock());
283 receiver_ = other.receiver_;
284 other.receiver_ = nullptr;
285 if (receiver_) {
286 receiver_->sender_ = this;
287 }
288 }
289
290 OnceRefSender(const OnceRefSender&) = delete;
291 OnceRefSender& operator=(const OnceRefSender&) = delete;
292 OnceRefSender& operator=(OnceRefSender&& other) = delete;
293
295 void Set(const T& value) {
296 std::lock_guard lock(sender_receiver_lock());
297 if (receiver_) {
298 *(receiver_->value_) = value;
299 std::move(receiver_->waker_).Wake();
300 receiver_->sender_ = nullptr;
301 receiver_->value_ = nullptr;
302 receiver_ = nullptr;
303 }
304 }
305
307 void Set(T&& value) {
308 std::lock_guard lock(sender_receiver_lock());
309 if (receiver_) {
310 *(receiver_->value_) = std::move(value);
311 std::move(receiver_->waker_).Wake();
312 receiver_->sender_ = nullptr;
313 receiver_->value_ = nullptr;
314 receiver_ = nullptr;
315 }
316 }
317
322 void ModifyUnsafe(pw::Function<void(T&)> func) {
323 std::lock_guard lock(sender_receiver_lock());
324 if (receiver_) {
325 // There is a risk of re-entrancy here if the user isn't careful.
326 func(*(receiver_->value_));
327 }
328 }
329
332 void Commit() {
333 std::lock_guard lock(sender_receiver_lock());
334 if (receiver_) {
335 std::move(receiver_->waker_).Wake();
336 receiver_->sender_ = nullptr;
337 receiver_->value_ = nullptr;
338 receiver_ = nullptr;
339 }
340 }
341
342 private:
343 template <typename U>
344 friend std::pair<OnceRefSender<U>, OnceRefReceiver<U>>
345 MakeOnceRefSenderAndReceiver(U&);
346 template <typename U>
347 friend void InitializeOnceRefSenderAndReceiver(OnceRefSender<U>& sender,
348 OnceRefReceiver<U>& receiver,
349 U& value);
350 friend class OnceRefReceiver<T>;
351
352 OnceRefSender(OnceRefReceiver<T>* receiver) : receiver_(receiver) {}
353
354 OnceRefReceiver<T>* receiver_ PW_GUARDED_BY(sender_receiver_lock()) = nullptr;
355};
356
357template <typename T>
358OnceRefReceiver<T>::~OnceRefReceiver() {
359 std::lock_guard lock(sender_receiver_lock());
360 if (sender_) {
361 sender_->receiver_ = nullptr;
362 }
363}
364
369template <typename T>
371 T& value) {
372 std::pair<OnceRefSender<T>, OnceRefReceiver<T>> send_recv;
373 InitializeOnceRefSenderAndReceiver(send_recv.first, send_recv.second, value);
374 return send_recv;
375}
376
381template <typename T>
383 OnceRefReceiver<T>& receiver,
385 // Disable lock analysis because these are fresh sender/receiver pairs and
386 // do not require a lock to initialize;
387 receiver.sender_ = &sender;
388 receiver.value_ = &value;
389 sender.receiver_ = &receiver;
390}
391
393
394} // namespace pw::async2
static constexpr Status Cancelled()
Definition: status.h:139
Definition: context.h:54
Definition: once_sender.h:42
friend std::pair< OnceSender< U >, OnceReceiver< U > > MakeOnceSenderAndReceiver()
Construct a pair of OnceSender and OnceReceiver.
Definition: once_sender.h:177
OnceReceiver(Args &&... value_args)
Definition: once_sender.h:49
PollResult< T > Pend(Context &cx)
Definition: once_sender.h:75
Definition: once_sender.h:205
Poll< Status > Pend(Context &cx)
Definition: once_sender.h:229
Definition: once_sender.h:269
void Set(const T &value)
Copy assigns the reference and awakens the receiver.
Definition: once_sender.h:295
void Set(T &&value)
Move assigns the reference and awakens the receiver.
Definition: once_sender.h:307
void Commit()
Definition: once_sender.h:332
void ModifyUnsafe(pw::Function< void(T &)> func)
Definition: once_sender.h:322
Definition: once_sender.h:108
friend std::pair< OnceSender< U >, OnceReceiver< U > > MakeOnceSenderAndReceiver()
Construct a pair of OnceSender and OnceReceiver.
Definition: once_sender.h:177
void emplace(Args &&... args)
Construct the sent value in place and wake the OnceReceiver.
Definition: once_sender.h:135
Definition: poll.h:60
Definition: waker.h:160
Definition: interrupt_spin_lock.h:50
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:271
#define PW_ASYNC_STORE_WAKER(context, waker_or_queue_out, wait_reason_string)
Definition: waker.h:60
constexpr Poll Ready()
Returns a value indicating completion.
Definition: poll.h:255
void InitializeOnceSenderAndReceiver(OnceSender< T > &sender, OnceReceiver< T > &receiver)
Initialize a pair of OnceSender and OnceReceiver.
Definition: once_sender.h:185
std::pair< OnceSender< T >, OnceReceiver< T > > MakeOnceSenderAndReceiver()
Construct a pair of OnceSender and OnceReceiver.
Definition: once_sender.h:177
std::pair< OnceRefSender< T >, OnceRefReceiver< T > > MakeOnceRefSenderAndReceiver(T &value)
Definition: once_sender.h:370
void InitializeOnceRefSenderAndReceiver(OnceRefSender< T > &sender, OnceRefReceiver< T > &receiver, T &value)
Definition: once_sender.h:382
fit::function_impl< function_internal::config::kInlineCallableSize, !function_internal::config::kEnableDynamicAllocation, FunctionType, PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE > Function
Definition: function.h:73
constexpr Status OkStatus()
Definition: status.h:450
#define PW_GUARDED_BY(x)
Definition: lock_annotations.h:60
#define PW_NO_LOCK_SAFETY_ANALYSIS
Definition: lock_annotations.h:292