C/C++ API Reference
Loading...
Searching...
No Matches
value_future.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#include <optional>
18#include <type_traits>
19
20#include "pw_assert/assert.h"
21#include "pw_async2/future.h"
22#include "pw_polyfill/language_feature_macros.h"
23#include "pw_sync/interrupt_spin_lock.h"
24
25namespace pw::async2 {
26namespace internal {
27
28inline sync::InterruptSpinLock& ValueProviderLock() {
29 PW_CONSTINIT static sync::InterruptSpinLock lock;
30 return lock;
31}
32
33bool PendValueFutureCore(FutureCore& core, Context& cx)
34 PW_EXCLUSIVE_LOCKS_REQUIRED(ValueProviderLock());
35
36} // namespace internal
37
38template <typename T>
39class ValueProvider;
40template <typename T>
41class BroadcastValueProvider;
42
44
50template <typename T>
52 public:
53 using value_type = T;
54
55 constexpr ValueFuture() = default;
56
57 ValueFuture(ValueFuture&& other) noexcept
58 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
59 *this = std::move(other);
60 }
61
62 ValueFuture& operator=(ValueFuture&& other) noexcept
63 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
64 if (this != &other) {
65 std::lock_guard lock(internal::ValueProviderLock());
66 core_ = std::move(other.core_);
67 value_ = std::move(other.value_);
68 }
69 return *this;
70 }
71
72 ~ValueFuture() PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
73 std::lock_guard lock(internal::ValueProviderLock());
74 core_.Unlist();
75 }
76
79 template <typename... Args>
80 static ValueFuture Resolved(Args&&... args) {
81 return ValueFuture(std::in_place, std::forward<Args>(args)...);
82 }
83
84 Poll<T> Pend(Context& cx) {
85 // ValueFuture uses a global lock so that futures don't have to access their
86 // provider to get a lock after they're completed. This ensures the
87 // ValueFuture never needs to access the provider.
88 //
89 // With some care (and complexity), the lock could be moved to the provider.
90 // A global lock is simpler and more efficient in practice.
91 std::lock_guard lock(internal::ValueProviderLock());
92 if (internal::PendValueFutureCore(core_, cx)) {
93 return Pending();
94 }
95 return Ready(std::move(*value_));
96 }
97
98 [[nodiscard]] bool is_pendable() const {
99 std::lock_guard lock(internal::ValueProviderLock());
100 return core_.is_pendable();
101 }
102
103 [[nodiscard]] bool is_complete() const {
104 std::lock_guard lock(internal::ValueProviderLock());
105 return core_.is_complete();
106 }
107
108 private:
109 friend class ValueProvider<T>;
110 friend class BroadcastValueProvider<T>;
111
112 template <typename... Args>
113 explicit ValueFuture(std::in_place_t, Args&&... args)
114 : core_(FutureState::kReadyForCompletion),
115 value_(std::in_place, std::forward<Args>(args)...) {}
116
117 ValueFuture(FutureState::Pending) : core_(FutureState::kPending) {}
118
119 template <typename... Args>
120 void ResolveLocked(Args&&... args)
121 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::ValueProviderLock()) {
122 // SAFETY: This is only called from FutureList with the lock held.
123 PW_DASSERT(!value_.has_value());
124 value_.emplace(std::forward<Args>(args)...);
125 core_.WakeAndMarkReady();
126 }
127
128 FutureCore core_ PW_GUARDED_BY(internal::ValueProviderLock());
129 std::optional<T> value_ PW_GUARDED_BY(internal::ValueProviderLock());
130};
131
134template <>
135class ValueFuture<void> {
136 public:
137 using value_type = void;
138
139 constexpr ValueFuture() = default;
140
141 ValueFuture(ValueFuture&& other) = default;
142
143 ValueFuture& operator=(ValueFuture&& other) = default;
144
145 ~ValueFuture() PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
146 std::lock_guard lock(internal::ValueProviderLock());
147 core_.Unlist();
148 }
149
150 Poll<> Pend(Context& cx) {
151 std::lock_guard lock(internal::ValueProviderLock());
152 if (internal::PendValueFutureCore(core_, cx)) {
153 return Pending();
154 }
155 return Ready();
156 }
157
158 [[nodiscard]] bool is_pendable() const { return core_.is_pendable(); }
159 [[nodiscard]] bool is_complete() const { return core_.is_complete(); }
160
161 static ValueFuture Resolved() {
162 return ValueFuture(FutureState::kReadyForCompletion);
163 }
164
165 private:
166 friend class ValueProvider<void>;
167 friend class BroadcastValueProvider<void>;
168
170 : core_(FutureState::kReadyForCompletion) {}
171
172 explicit ValueFuture(FutureState::Pending) : core_(FutureState::kPending) {}
173
174 FutureCore core_;
175};
176
179
181template <typename T>
183
198template <typename T>
200 public:
201 constexpr BroadcastValueProvider() = default;
202
204 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
205 std::lock_guard lock(internal::ValueProviderLock());
206 list_ = std::move(other.list_);
207 }
208
209 BroadcastValueProvider& operator=(BroadcastValueProvider&& other) noexcept
210 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
211 if (this != &other) {
212 std::lock_guard lock(internal::ValueProviderLock());
213 PW_ASSERT(list_.empty()); // ensure any futures were resolved
214 list_ = std::move(other.list_);
215 }
216 return *this;
217 }
218
220 BroadcastValueProvider& operator=(const BroadcastValueProvider&) = delete;
221
222 ~BroadcastValueProvider() { PW_ASSERT(list_.empty()); }
223
228 ValueFuture<T> future(FutureState::kPending);
229 {
230 std::lock_guard lock(internal::ValueProviderLock());
231 list_.Push(future.core_);
232 }
233 return future;
234 }
235
237 template <typename U = T, std::enable_if_t<!std::is_void_v<U>, int> = 0>
238 void Resolve(const U& value) {
239 std::lock_guard lock(internal::ValueProviderLock());
240 list_.ResolveAllWith(
241 [&](ValueFuture<T>& future)
242 PW_NO_LOCK_SAFETY_ANALYSIS { future.ResolveLocked(value); });
243 }
244
246 template <typename U = T, std::enable_if_t<std::is_void_v<U>, int> = 0>
247 void Resolve() {
248 std::lock_guard lock(internal::ValueProviderLock());
249 list_.ResolveAll();
250 }
251
252 private:
254 PW_GUARDED_BY(internal::ValueProviderLock());
255};
256
268template <typename T>
270 public:
271 constexpr ValueProvider() = default;
272
273 ValueProvider(ValueProvider&& other) noexcept
274 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
275 std::lock_guard lock(internal::ValueProviderLock());
276 list_ = std::move(other.list_);
277 }
278
279 ValueProvider& operator=(ValueProvider&& other) noexcept
280 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
281 if (this != &other) {
282 std::lock_guard lock(internal::ValueProviderLock());
283 PW_ASSERT(list_.empty()); // ensure any futures were resolved
284 list_ = std::move(other.list_);
285 }
286 return *this;
287 }
288
289 ValueProvider(const ValueProvider&) = delete;
290 ValueProvider& operator=(const ValueProvider&) = delete;
291
292 ~ValueProvider() { PW_ASSERT(list_.empty()); }
293
298 ValueFuture<T> future(FutureState::kPending);
299 {
300 std::lock_guard lock(internal::ValueProviderLock());
301 list_.PushRequireEmpty(future.core_);
302 }
303 return future;
304 }
305
310 std::optional<ValueFuture<T>> TryGet() {
311 ValueFuture<T> future(FutureState::kPending);
312 {
313 std::lock_guard lock(internal::ValueProviderLock());
314 if (!list_.PushIfEmpty(future.core_)) {
315 return std::nullopt;
316 }
317 }
318 return future;
319 }
320
322 bool has_future() const {
323 std::lock_guard lock(internal::ValueProviderLock());
324 return !list_.empty();
325 }
326
329 template <typename... Args,
330 typename U = T,
331 std::enable_if_t<!std::is_void_v<U>, int> = 0>
332 void Resolve(Args&&... args) {
333 std::lock_guard lock(internal::ValueProviderLock());
334 if (ValueFuture<T>* future = list_.PopIfAvailable(); future != nullptr) {
335 future->ResolveLocked(std::forward<Args>(args)...);
336 };
337 }
338
340 template <typename U = T, std::enable_if_t<std::is_void_v<U>, int> = 0>
341 void Resolve() {
342 std::lock_guard lock(internal::ValueProviderLock());
343 list_.ResolveOneIfAvailable();
344 }
345
346 private:
348 PW_GUARDED_BY(internal::ValueProviderLock());
349};
350
355template <typename T>
357 public:
358 OptionalValueProvider() = default;
359
361 OptionalValueProvider& operator=(OptionalValueProvider&& other) {
362 Cancel();
363 provider_ = std::move(other.provider_);
364 return *this;
365 }
366
368 OptionalValueProvider& operator=(const OptionalValueProvider&) = delete;
369
371
374 OptionalValueFuture<T> Get() { return provider_.Get(); }
375
377 template <typename... Args>
378 void Resolve(Args&&... args) {
379 provider_.Resolve(std::in_place, std::forward<Args>(args)...);
380 }
381
383 void Cancel() { provider_.Resolve(std::nullopt); }
384
385 private:
387};
388
393template <typename T>
395 public:
397
401 Cancel();
402 provider_ = std::move(other.provider_);
403 return *this;
404 }
405
407 delete;
409 const OptionalBroadcastValueProvider&) = delete;
410
412
415 OptionalValueFuture<T> Get() { return provider_.Get(); }
416
418 void Resolve(const T& value) { provider_.Resolve(value); }
419
421 void Cancel() { provider_.Resolve(std::nullopt); }
422
423 private:
425};
426
428
429} // namespace pw::async2
Definition: value_future.h:199
ValueFuture< T > Get()
Definition: value_future.h:227
void Resolve(const U &value)
Resolves every pending ValueFuture with a copy of the provided value.
Definition: value_future.h:238
void Resolve()
Resolves every pending ValueFuture.
Definition: value_future.h:247
Definition: context.h:46
Definition: future.h:399
Definition: future.h:219
void WakeAndMarkReady()
Definition: future.h:277
constexpr bool is_pendable() const
Definition: future.h:246
constexpr bool is_complete() const
Definition: future.h:251
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:300
Pending
Tag for constructing an active future, for which Pend may be called.
Definition: future.h:114
ReadyForCompletion
Definition: future.h:118
Definition: value_future.h:394
OptionalValueFuture< T > Get()
Definition: value_future.h:415
void Resolve(const T &value)
Resolves all pending ValueFutures with the provided value.
Definition: value_future.h:418
void Cancel()
Resolves all pending ValueFutures with std::nullopt.
Definition: value_future.h:421
Definition: value_future.h:356
void Resolve(Args &&... args)
Resolves the pending ValueFuture by constructing it in-place.
Definition: value_future.h:378
void Cancel()
Resolves the pending ValueFuture with std::nullopt.
Definition: value_future.h:383
OptionalValueFuture< T > Get()
Definition: value_future.h:374
Definition: poll.h:138
Definition: value_future.h:135
Definition: value_future.h:51
static ValueFuture Resolved(Args &&... args)
Definition: value_future.h:80
Definition: value_future.h:269
ValueFuture< T > Get()
Definition: value_future.h:297
void Resolve(Args &&... args)
Definition: value_future.h:332
bool has_future() const
Returns true if the provider stores a pending future.
Definition: value_future.h:322
void Resolve()
Resolves the pending ValueFuture.
Definition: value_future.h:341
std::optional< ValueFuture< T > > TryGet()
Definition: value_future.h:310
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:353
constexpr Poll Ready()
Returns a value indicating completion.
Definition: poll.h:337
#define PW_CONSTINIT
Definition: language_feature_macros.h:52
#define PW_GUARDED_BY(x)
Definition: lock_annotations.h:60
#define PW_NO_LOCK_SAFETY_ANALYSIS
Definition: lock_annotations.h:292
#define PW_EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: lock_annotations.h:146
#define PW_LOCKS_EXCLUDED(...)
Definition: lock_annotations.h:176