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 <optional>
17#include <type_traits>
18
19#include "pw_assert/assert.h"
20#include "pw_async2/future.h"
21#include "pw_polyfill/language_feature_macros.h"
22#include "pw_sync/interrupt_spin_lock.h"
23
24namespace pw::async2 {
25namespace internal {
26
27inline sync::InterruptSpinLock& ValueProviderLock() {
28 PW_CONSTINIT static sync::InterruptSpinLock lock;
29 return lock;
30}
31
32} // namespace internal
33
34template <typename T>
35class ValueProvider;
36template <typename T>
37class BroadcastValueProvider;
38
40
46template <typename T>
48 public:
49 using value_type = T;
50
51 constexpr ValueFuture() = default;
52
53 ValueFuture(ValueFuture&& other) noexcept
54 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
55 *this = std::move(other);
56 }
57
58 ValueFuture& operator=(ValueFuture&& other) noexcept
59 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
60 if (this != &other) {
61 std::lock_guard lock(internal::ValueProviderLock());
62 core_ = std::move(other.core_);
63 value_ = std::move(other.value_);
64 }
65 return *this;
66 }
67
68 ~ValueFuture() PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
69 std::lock_guard lock(internal::ValueProviderLock());
70 core_.Unlist();
71 }
72
75 template <typename... Args>
76 static ValueFuture Resolved(Args&&... args) {
77 return ValueFuture(std::in_place, std::forward<Args>(args)...);
78 }
79
80 Poll<T> Pend(Context& cx) {
81 // ValueFuture uses a global lock so that futures don't have to access their
82 // provider to get a lock after they're completed. This ensures the
83 // ValueFuture never needs to access the provider.
84 //
85 // With some care (and complexity), the lock could be moved to the provider.
86 // A global lock is simpler and more efficient in practice.
87 std::lock_guard lock(internal::ValueProviderLock());
88 return core_.DoPend<ValueFuture<T>>(*this, cx);
89 }
90
91 [[nodiscard]] bool is_pendable() const {
92 std::lock_guard lock(internal::ValueProviderLock());
93 return core_.is_pendable();
94 }
95
96 [[nodiscard]] bool is_complete() const {
97 std::lock_guard lock(internal::ValueProviderLock());
98 return core_.is_complete();
99 }
100
101 private:
102 friend class FutureCore;
103 friend class ValueProvider<T>;
104 friend class BroadcastValueProvider<T>;
105
106 static constexpr char kWaitReason[] = "ValueFuture";
107
108 template <typename... Args>
109 explicit ValueFuture(std::in_place_t, Args&&... args)
110 : core_(FutureState::kReadyForCompletion),
111 value_(std::in_place, std::forward<Args>(args)...) {}
112
113 ValueFuture(FutureState::Pending) : core_(FutureState::kPending) {}
114
115 Poll<T> DoPend(Context&)
116 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::ValueProviderLock()) {
117 if (!core_.is_ready()) {
118 return Pending();
119 }
120
121 return Ready(std::move(*value_));
122 }
123
124 template <typename... Args>
125 void ResolveLocked(Args&&... args)
126 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::ValueProviderLock()) {
127 // SAFETY: This is only called from FutureList with the lock held.
128 PW_DASSERT(!value_.has_value());
129 value_.emplace(std::forward<Args>(args)...);
130 core_.WakeAndMarkReady();
131 }
132
133 FutureCore core_ PW_GUARDED_BY(internal::ValueProviderLock());
134 std::optional<T> value_ PW_GUARDED_BY(internal::ValueProviderLock());
135};
136
139template <>
140class ValueFuture<void> {
141 public:
142 using value_type = void;
143
144 constexpr ValueFuture() = default;
145
146 ValueFuture(ValueFuture&& other) = default;
147
148 ValueFuture& operator=(ValueFuture&& other) = default;
149
150 ~ValueFuture() PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
151 std::lock_guard lock(internal::ValueProviderLock());
152 core_.Unlist();
153 }
154
155 Poll<> Pend(Context& cx) {
156 std::lock_guard lock(internal::ValueProviderLock());
157 return core_.DoPend<ValueFuture<void>>(*this, cx);
158 }
159
160 [[nodiscard]] bool is_pendable() const { return core_.is_pendable(); }
161 [[nodiscard]] bool is_complete() const { return core_.is_complete(); }
162
163 static ValueFuture Resolved() {
164 return ValueFuture(FutureState::kReadyForCompletion);
165 }
166
167 static constexpr char kWaitReason[] = "ValueFuture<void>";
168
169 private:
170 friend class FutureCore;
171 friend class ValueProvider<void>;
172 friend class BroadcastValueProvider<void>;
173
175 : core_(FutureState::kReadyForCompletion) {}
176
177 explicit ValueFuture(FutureState::Pending) : core_(FutureState::kPending) {}
178
179 Poll<> DoPend(Context&) {
180 if (!core_.is_ready()) {
181 return Pending();
182 }
183 return Ready();
184 }
185
186 FutureCore core_;
187};
188
191
193template <typename T>
195
210template <typename T>
212 public:
213 constexpr BroadcastValueProvider() = default;
214
216 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
217 std::lock_guard lock(internal::ValueProviderLock());
218 list_ = std::move(other.list_);
219 }
220
221 BroadcastValueProvider& operator=(BroadcastValueProvider&& other) noexcept
222 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
223 if (this != &other) {
224 std::lock_guard lock(internal::ValueProviderLock());
225 PW_ASSERT(list_.empty()); // ensure any futures were resolved
226 list_ = std::move(other.list_);
227 }
228 return *this;
229 }
230
232 BroadcastValueProvider& operator=(const BroadcastValueProvider&) = delete;
233
234 ~BroadcastValueProvider() { PW_ASSERT(list_.empty()); }
235
240 ValueFuture<T> future(FutureState::kPending);
241 {
242 std::lock_guard lock(internal::ValueProviderLock());
243 list_.Push(future.core_);
244 }
245 return future;
246 }
247
249 template <typename U = T, std::enable_if_t<!std::is_void_v<U>, int> = 0>
250 void Resolve(const U& value) {
251 std::lock_guard lock(internal::ValueProviderLock());
252 list_.ResolveAllWith(
253 [&](ValueFuture<T>& future)
254 PW_NO_LOCK_SAFETY_ANALYSIS { future.ResolveLocked(value); });
255 }
256
258 template <typename U = T, std::enable_if_t<std::is_void_v<U>, int> = 0>
259 void Resolve() {
260 std::lock_guard lock(internal::ValueProviderLock());
261 list_.ResolveAll();
262 }
263
264 private:
266 PW_GUARDED_BY(internal::ValueProviderLock());
267};
268
280template <typename T>
282 public:
283 constexpr ValueProvider() = default;
284
285 ValueProvider(ValueProvider&& other) noexcept
286 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
287 std::lock_guard lock(internal::ValueProviderLock());
288 list_ = std::move(other.list_);
289 }
290
291 ValueProvider& operator=(ValueProvider&& other) noexcept
292 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
293 if (this != &other) {
294 std::lock_guard lock(internal::ValueProviderLock());
295 PW_ASSERT(list_.empty()); // ensure any futures were resolved
296 list_ = std::move(other.list_);
297 }
298 return *this;
299 }
300
301 ValueProvider(const ValueProvider&) = delete;
302 ValueProvider& operator=(const ValueProvider&) = delete;
303
304 ~ValueProvider() { PW_ASSERT(list_.empty()); }
305
310 ValueFuture<T> future(FutureState::kPending);
311 {
312 std::lock_guard lock(internal::ValueProviderLock());
313 list_.PushRequireEmpty(future);
314 }
315 return future;
316 }
317
322 std::optional<ValueFuture<T>> TryGet() {
323 ValueFuture<T> future(FutureState::kPending);
324 {
325 std::lock_guard lock(internal::ValueProviderLock());
326 if (!list_.PushIfEmpty(future.core_)) {
327 return std::nullopt;
328 }
329 }
330 return future;
331 }
332
334 bool has_future() const {
335 std::lock_guard lock(internal::ValueProviderLock());
336 return !list_.empty();
337 }
338
341 template <typename... Args,
342 typename U = T,
343 std::enable_if_t<!std::is_void_v<U>, int> = 0>
344 void Resolve(Args&&... args) {
345 std::lock_guard lock(internal::ValueProviderLock());
346 if (ValueFuture<T>* future = list_.PopIfAvailable(); future != nullptr) {
347 future->ResolveLocked(std::forward<Args>(args)...);
348 };
349 }
350
352 template <typename U = T, std::enable_if_t<std::is_void_v<U>, int> = 0>
353 void Resolve() {
354 std::lock_guard lock(internal::ValueProviderLock());
355 list_.ResolveOneIfAvailable();
356 }
357
358 private:
360 PW_GUARDED_BY(internal::ValueProviderLock());
361};
362
367template <typename T>
369 public:
370 OptionalValueProvider() = default;
371
373 OptionalValueProvider& operator=(OptionalValueProvider&& other) {
374 Cancel();
375 provider_ = std::move(other.provider_);
376 return *this;
377 }
378
380 OptionalValueProvider& operator=(const OptionalValueProvider&) = delete;
381
383
386 OptionalValueFuture<T> Get() { return provider_.Get(); }
387
389 template <typename... Args>
390 void Resolve(Args&&... args) {
391 provider_.Resolve(std::in_place, std::forward<Args>(args)...);
392 }
393
395 void Cancel() { provider_.Resolve(std::nullopt); }
396
397 private:
399};
400
405template <typename T>
407 public:
409
413 Cancel();
414 provider_ = std::move(other.provider_);
415 return *this;
416 }
417
419 delete;
421 const OptionalBroadcastValueProvider&) = delete;
422
424
427 OptionalValueFuture<T> Get() { return provider_.Get(); }
428
430 void Resolve(const T& value) { provider_.Resolve(value); }
431
433 void Cancel() { provider_.Resolve(std::nullopt); }
434
435 private:
437};
438
440
441} // namespace pw::async2
Definition: value_future.h:211
ValueFuture< T > Get()
Definition: value_future.h:239
void Resolve(const U &value)
Resolves every pending ValueFuture with a copy of the provided value.
Definition: value_future.h:250
void Resolve()
Resolves every pending ValueFuture.
Definition: value_future.h:259
Definition: context.h:46
Definition: future.h:386
Definition: future.h:212
void WakeAndMarkReady()
Definition: future.h:267
constexpr bool is_pendable() const
Definition: future.h:239
constexpr bool is_complete() const
Definition: future.h:244
auto DoPend(FutureType &future, Context &cx)
Definition: future.h:305
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:287
constexpr bool is_ready() const
Definition: future.h:254
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:406
OptionalValueFuture< T > Get()
Definition: value_future.h:427
void Resolve(const T &value)
Resolves all pending ValueFutures with the provided value.
Definition: value_future.h:430
void Cancel()
Resolves all pending ValueFutures with std::nullopt.
Definition: value_future.h:433
Definition: value_future.h:368
void Resolve(Args &&... args)
Resolves the pending ValueFuture by constructing it in-place.
Definition: value_future.h:390
void Cancel()
Resolves the pending ValueFuture with std::nullopt.
Definition: value_future.h:395
OptionalValueFuture< T > Get()
Definition: value_future.h:386
Definition: poll.h:138
Definition: value_future.h:140
Definition: value_future.h:47
static ValueFuture Resolved(Args &&... args)
Definition: value_future.h:76
Definition: value_future.h:281
ValueFuture< T > Get()
Definition: value_future.h:309
void Resolve(Args &&... args)
Definition: value_future.h:344
bool has_future() const
Returns true if the provider stores a pending future.
Definition: value_future.h:334
void Resolve()
Resolves the pending ValueFuture.
Definition: value_future.h:353
std::optional< ValueFuture< T > > TryGet()
Definition: value_future.h:322
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