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 <type_traits>
17
18#include "pw_assert/assert.h"
19#include "pw_async2/future.h"
20#include "pw_polyfill/language_feature_macros.h"
21#include "pw_sync/interrupt_spin_lock.h"
22
23namespace pw::async2 {
24namespace internal {
25
26inline sync::InterruptSpinLock& ValueProviderLock() {
27 PW_CONSTINIT static sync::InterruptSpinLock lock;
28 return lock;
29}
30
31} // namespace internal
32
33template <typename T>
34class ValueProvider;
35template <typename T>
36class BroadcastValueProvider;
37
39
45template <typename T>
47 public:
48 using value_type = T;
49
50 ValueFuture(ValueFuture&& other) noexcept
51 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
52 *this = std::move(other);
53 }
54
55 ValueFuture& operator=(ValueFuture&& other) noexcept
56 PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
57 if (this != &other) {
58 std::lock_guard lock(internal::ValueProviderLock());
59 core_ = std::move(other.core_);
60 value_ = std::move(other.value_);
61 }
62 return *this;
63 }
64
65 ~ValueFuture() PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
66 std::lock_guard lock(internal::ValueProviderLock());
67 core_.Unlist();
68 }
69
72 template <typename... Args>
73 static ValueFuture Resolved(Args&&... args) {
74 return ValueFuture(std::in_place, std::forward<Args>(args)...);
75 }
76
77 Poll<T> Pend(Context& cx) {
78 // ValueFuture uses a global lock so that futures don't have to access their
79 // provider to get a lock after they're completed. This ensures the
80 // ValueFuture never needs to access the provider.
81 //
82 // With some care (and complexity), the lock could be moved to the provider.
83 // A global lock is simpler and more efficient in practice.
84 std::lock_guard lock(internal::ValueProviderLock());
85 return core_.DoPend<ValueFuture<T>>(*this, cx);
86 }
87
88 [[nodiscard]] bool is_complete() const {
89 std::lock_guard lock(internal::ValueProviderLock());
90 return core_.is_complete();
91 }
92
93 private:
94 friend class FutureCore;
95 friend class ValueProvider<T>;
96 friend class BroadcastValueProvider<T>;
97
98 static constexpr char kWaitReason[] = "ValueFuture";
99
100 template <typename... Args>
101 explicit ValueFuture(std::in_place_t, Args&&... args)
102 : core_(FutureCore::kReadyForCompletion),
103 value_(std::in_place, std::forward<Args>(args)...) {}
104
105 ValueFuture(FutureCore::Pending) : core_(FutureCore::kPending) {}
106
107 Poll<T> DoPend(Context&)
108 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::ValueProviderLock()) {
109 if (!core_.is_ready()) {
110 return Pending();
111 }
112
113 return Ready(std::move(*value_));
114 }
115
116 template <typename... Args>
117 void ResolveLocked(Args&&... args)
118 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::ValueProviderLock()) {
119 // SAFETY: This is only called from FutureList with the lock held.
120 PW_DASSERT(!value_.has_value());
121 value_.emplace(std::forward<Args>(args)...);
122 core_.WakeAndMarkReady();
123 }
124
125 FutureCore core_ PW_GUARDED_BY(internal::ValueProviderLock());
126 std::optional<T> value_ PW_GUARDED_BY(internal::ValueProviderLock());
127};
128
131template <>
132class ValueFuture<void> {
133 public:
134 using value_type = ReadyType;
135
136 ValueFuture(ValueFuture&& other) = default;
137
138 ValueFuture& operator=(ValueFuture&& other) = default;
139
140 ~ValueFuture() PW_LOCKS_EXCLUDED(internal::ValueProviderLock()) {
141 std::lock_guard lock(internal::ValueProviderLock());
142 core_.Unlist();
143 }
144
145 Poll<> Pend(Context& cx) {
146 std::lock_guard lock(internal::ValueProviderLock());
147 return core_.DoPend<ValueFuture<void>>(*this, cx);
148 }
149
150 [[nodiscard]] bool is_complete() const { return core_.is_complete(); }
151
152 static ValueFuture Resolved() {
153 return ValueFuture(FutureCore::kReadyForCompletion);
154 }
155
156 static constexpr char kWaitReason[] = "ValueFuture<void>";
157
158 private:
159 friend class FutureCore;
160 friend class ValueProvider<void>;
161 friend class BroadcastValueProvider<void>;
162
164 : core_(FutureCore::kReadyForCompletion) {}
165
166 explicit ValueFuture(FutureCore::Pending) : core_(FutureCore::kPending) {}
167
168 Poll<> DoPend(Context&) {
169 if (!core_.is_ready()) {
170 return Pending();
171 }
172 return Ready();
173 }
174
175 FutureCore core_;
176};
177
180
189template <typename T>
191 public:
192 constexpr BroadcastValueProvider() = default;
193
194 ~BroadcastValueProvider() { PW_ASSERT(list_.empty()); }
195
200 ValueFuture<T> future(FutureCore::kPending);
201 {
202 std::lock_guard lock(internal::ValueProviderLock());
203 list_.Push(future.core_);
204 }
205 return future;
206 }
207
209 template <typename U = T, std::enable_if_t<!std::is_void_v<U>, int> = 0>
210 void Resolve(const U& value) {
211 std::lock_guard lock(internal::ValueProviderLock());
212 list_.ResolveAllWith(
213 [&](ValueFuture<T>& future)
214 PW_NO_LOCK_SAFETY_ANALYSIS { future.ResolveLocked(value); });
215 }
216
218 template <typename U = T, std::enable_if_t<std::is_void_v<U>, int> = 0>
219 void Resolve() {
220 std::lock_guard lock(internal::ValueProviderLock());
221 list_.ResolveAll();
222 }
223
224 private:
226 PW_GUARDED_BY(internal::ValueProviderLock());
227};
228
235template <typename T>
237 public:
238 constexpr ValueProvider() = default;
239
240 ~ValueProvider() { PW_ASSERT(list_.empty()); }
241
246 ValueFuture<T> future(FutureCore::kPending);
247 {
248 std::lock_guard lock(internal::ValueProviderLock());
249 list_.PushRequireEmpty(future);
250 }
251 return future;
252 }
253
258 std::optional<ValueFuture<T>> TryGet() {
259 ValueFuture<T> future(FutureCore::kPending);
260 {
261 std::lock_guard lock(internal::ValueProviderLock());
262 if (!list_.PushIfEmpty(future.core_)) {
263 return std::nullopt;
264 }
265 }
266 return future;
267 }
268
270 bool has_future() const {
271 std::lock_guard lock(internal::ValueProviderLock());
272 return !list_.empty();
273 }
274
277 template <typename... Args,
278 typename U = T,
279 std::enable_if_t<!std::is_void_v<U>, int> = 0>
280 void Resolve(Args&&... args) {
281 std::lock_guard lock(internal::ValueProviderLock());
282 if (ValueFuture<T>* future = list_.PopIfAvailable(); future != nullptr) {
283 future->ResolveLocked(std::forward<Args>(args)...);
284 };
285 }
286
288 template <typename U = T, std::enable_if_t<std::is_void_v<U>, int> = 0>
289 void Resolve() {
290 std::lock_guard lock(internal::ValueProviderLock());
291 list_.ResolveOneIfAvailable();
292 }
293
294 private:
296 PW_GUARDED_BY(internal::ValueProviderLock());
297};
298
300
301} // namespace pw::async2
Definition: value_future.h:190
ValueFuture< T > Get()
Definition: value_future.h:199
void Resolve(const U &value)
Resolves every pending ValueFuture with a copy of the provided value.
Definition: value_future.h:210
void Resolve()
Resolves every pending ValueFuture.
Definition: value_future.h:219
Definition: context.h:54
Definition: future.h:598
Definition: future.h:440
Pending
Tag type to construct an active FutureCore. Pend may be called.
Definition: future.h:453
bool is_ready() const
Definition: future.h:482
void WakeAndMarkReady()
Definition: future.h:490
bool is_complete() const
Definition: future.h:476
ReadyForCompletion
Definition: future.h:450
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:496
Definition: poll.h:60
Definition: value_future.h:132
Definition: value_future.h:46
static ValueFuture Resolved(Args &&... args)
Definition: value_future.h:73
Definition: value_future.h:236
ValueFuture< T > Get()
Definition: value_future.h:245
void Resolve(Args &&... args)
Definition: value_future.h:280
bool has_future() const
Returns true if the provider stores a pending future.
Definition: value_future.h:270
void Resolve()
Resolves the pending ValueFuture.
Definition: value_future.h:289
std::optional< ValueFuture< T > > TryGet()
Definition: value_future.h:258
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:271
constexpr Poll Ready()
Returns a value indicating completion.
Definition: poll.h:255
#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
Definition: poll.h:36