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