C/C++ API Reference
Loading...
Searching...
No Matches
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#ifdef __cpp_concepts
17#include <concepts>
18#endif // __cpp_concepts
19
20#include <mutex>
21#include <optional>
22#include <type_traits>
23
24#include "lib/stdcompat/utility.h"
25#include "pw_assert/assert.h"
26#include "pw_async2/context.h"
27#include "pw_async2/poll.h"
28#include "pw_containers/intrusive_list.h"
29#include "pw_memory/container_of.h"
30#include "pw_sync/interrupt_spin_lock.h"
31
32namespace pw::async2 {
33
35
36#ifdef __cpp_concepts
37
46template <typename T>
47concept Future = requires {
51 typename T::value_type;
52
54 static_cast<bool (T::*)() const>(&T::is_pendable);
55
57 static_cast<bool (T::*)() const>(&T::is_complete);
58
63 static_cast<Poll<typename T::value_type> (T::*)(Context&)>(&T::Pend);
64
67} && std::default_initializable<T> && std::destructible<T> && std::movable<T>;
68
69#else // C++17 version
70
71namespace internal {
72
73template <typename T, typename = void>
74struct is_future : std::false_type {};
75
76template <typename T>
77struct is_future<
78 T,
79 std::void_t<typename T::value_type,
80 decltype(std::declval<T&>().Pend(std::declval<Context&>())),
81 decltype(std::declval<const T&>().is_pendable()),
82 decltype(std::declval<const T&>().is_complete())>>
83 : std::conjunction<
84 std::is_convertible<decltype(&T::is_pendable), bool (T::*)() const>,
85 std::is_convertible<decltype(&T::is_complete), bool (T::*)() const>,
86 std::is_convertible<decltype(&T::Pend),
87 Poll<typename T::value_type> (T::*)(Context&)>,
88 std::is_default_constructible<T>,
89 std::is_destructible<T>> {};
90
91} // namespace internal
92
93// This variable is named as a type to match the C++20 concept. This makes it
94// possible to use `Future` in static asserts in either C++17 or C++20.
95template <typename T>
96constexpr bool Future =
97 internal::is_future<std::remove_cv_t<std::remove_reference_t<T>>>::value;
98
99#endif // __cpp_concepts
100
104template <typename T>
105using FutureValue = std::conditional_t<std::is_void_v<typename T::value_type>,
106 ReadyType,
107 typename T::value_type>;
108
112 public:
114 enum Pending { kPending };
115
118 enum ReadyForCompletion { kReadyForCompletion };
119
122 constexpr FutureState() : state_(State::kEmpty) {}
123
125 constexpr FutureState(Pending) : state_(State::kPending) {}
126
130 constexpr FutureState(ReadyForCompletion) : state_(State::kReady) {}
131
132 constexpr FutureState(const FutureState&) = delete;
133 constexpr FutureState& operator=(const FutureState&) = delete;
134
137 constexpr FutureState(FutureState&& other)
138 : state_(cpp20::exchange(other.state_, State::kEmpty)) {}
139
140 constexpr FutureState& operator=(FutureState&& other) {
141 state_ = cpp20::exchange(other.state_, State::kEmpty);
142 return *this;
143 }
144
145 friend constexpr bool operator==(const FutureState& lhs,
146 const FutureState& rhs) {
147 return lhs.state_ == rhs.state_;
148 }
149
150 friend constexpr bool operator!=(const FutureState& lhs,
151 const FutureState& rhs) {
152 return lhs.state_ != rhs.state_;
153 }
154
156 [[nodiscard]] constexpr bool is_pendable() const {
157 return state_ > State::kComplete;
158 }
159
162 [[nodiscard]] constexpr bool is_complete() const {
163 return state_ == State::kComplete;
164 }
165
169 [[nodiscard]] constexpr bool is_ready() const {
170 return state_ == State::kReady;
171 }
172
175 [[nodiscard]] constexpr bool is_initialized() const {
176 return state_ != State::kEmpty;
177 }
178
179 void MarkReady() { state_ = State::kReady; }
180
181 void MarkComplete() { state_ = State::kComplete; }
182
183 private:
184 enum class State : unsigned char {
185 // The future is in a default constructed, empty state. It does not
186 // represent an async operation and `Pend()` cannot be called.
187 kEmpty,
188
189 // A previous call to `Pend()` returned `Ready()`.
190 kComplete,
191
192 // The next call to `Pend()` will return `Ready()`.
193 kReady,
194
195 // `Pend()` may be called to advance the operation represented by this
196 // future. `Pend()` may return `Ready()` or `Pending()`.
197 kPending,
198 } state_;
199};
200
212class FutureCore : public IntrusiveForwardList<FutureCore>::Item {
213 public:
214 constexpr FutureCore() = default;
215
216 FutureCore(FutureCore&& other) noexcept;
217
218 FutureCore& operator=(FutureCore&& other) noexcept;
219
226 : state_(FutureState::kPending) {}
227
231 : state_(FutureState::kReadyForCompletion) {}
232
233 FutureCore(const FutureCore&) = delete;
234 FutureCore& operator=(const FutureCore&) = delete;
235
236 ~FutureCore() = default;
237
239 [[nodiscard]] constexpr bool is_pendable() const {
240 return state_.is_pendable();
241 }
242
244 [[nodiscard]] constexpr bool is_complete() const {
245 return state_.is_complete();
246 }
247
254 [[nodiscard]] constexpr bool is_ready() const { return state_.is_ready(); }
255
257 [[nodiscard]] constexpr bool is_initialized() const {
258 return state_.is_initialized();
259 }
260
263 void Wake() { waker_.Wake(); }
264
268 Wake();
269 state_.MarkReady();
270 }
271
277 Waker& waker() { return waker_; }
278
284 void MarkComplete() { state_.MarkComplete(); }
285
287 void Unlist() { unlist(); }
288
290 void Reset() {
291 Unlist();
292 state_ = FutureState();
293 }
294
304 template <typename FutureType>
305 auto DoPend(FutureType& future, Context& cx) PW_NO_LOCK_SAFETY_ANALYSIS {
306 PW_ASSERT(is_pendable());
307
308 auto poll = future.DoPend(cx);
309 if (poll.IsPending()) {
310 PW_ASYNC_STORE_WAKER(cx, waker_, FutureType::kWaitReason);
311 } else {
312 MarkComplete();
313 }
314
315 return poll;
316 }
317
319 [[nodiscard]] bool in_list() const { return !unlisted(); }
320
321 private:
322 friend class BaseFutureList; // for IntrusiveForwardList::Item functions
323
324 Waker waker_;
325 FutureState state_;
326};
327
336 public:
337 constexpr BaseFutureList() = default;
338
339 bool empty() const { return list_.empty(); }
340
341 void Push(FutureCore& future) { containers::PushBackSlow(list_, future); }
342
343 void PushRequireEmpty(FutureCore& future);
344
345 bool PushIfEmpty(FutureCore& future);
346
347 FutureCore* PopIfAvailable() { return list_.empty() ? nullptr : &Pop(); }
348
349 FutureCore& Pop() {
350 FutureCore& future = list_.front();
351 list_.pop_front();
352 return future;
353 }
354
357 void ResolveOne() {
358 list_.front().WakeAndMarkReady();
359 list_.pop_front();
360 }
361
362 void ResolveOneIfAvailable() {
363 if (!list_.empty()) {
364 ResolveOne();
365 }
366 }
367
370
371 protected:
372 IntrusiveForwardList<FutureCore>& list() { return list_; }
373
374 private:
376};
377
385template <auto kGetFutureImpl, auto kGetFutureCore>
387 public:
388 using value_type = std::remove_reference_t<decltype(*kGetFutureImpl(
389 std::declval<FutureCore*>()))>;
390 using pointer = value_type*;
391 using reference = value_type&;
392
393 constexpr CustomFutureList() = default;
394
395 void Push(FutureCore& future) { BaseFutureList::Push(future); }
396 void Push(reference future) { Push(kGetFutureCore(future)); }
397
398 void PushRequireEmpty(FutureCore& future) {
399 BaseFutureList::PushRequireEmpty(future);
400 }
401 void PushRequireEmpty(reference future) {
402 PushRequireEmpty(kGetFutureCore(future));
403 }
404
405 bool PushIfEmpty(FutureCore& future) {
406 return BaseFutureList::PushIfEmpty(future);
407 }
408 bool PushIfEmpty(reference future) {
409 return PushIfEmpty(kGetFutureCore(future));
410 }
411
412 pointer PopIfAvailable() {
413 return kGetFutureImpl(BaseFutureList::PopIfAvailable());
414 }
415
416 reference Pop() { return *kGetFutureImpl(BaseFutureList::PopIfAvailable()); }
417
418 template <typename Resolver>
419 void ResolveAllWith(Resolver&& resolver) {
420 while (!list().empty()) {
421 resolver(Pop());
422 }
423 }
424
425 template <typename Resolver>
426 void ResolveOneWith(Resolver&& resolver) {
427 if (!list().empty()) {
428 resolver(Pop());
429 }
430 }
431};
432
437template <auto kMemberPtr>
440
442
443} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: future.h:335
void ResolveOne()
Definition: future.h:357
void ResolveAll()
Pops all futures and calls WakeAndMarkReady() on them.
Definition: context.h:46
Definition: future.h:386
Definition: future.h:212
void WakeAndMarkReady()
Definition: future.h:267
constexpr bool is_initialized() const
Definition: future.h:257
Waker & waker()
Definition: future.h:277
void Wake()
Definition: future.h:263
constexpr bool is_pendable() const
Definition: future.h:239
void MarkComplete()
Definition: future.h:284
constexpr FutureCore(FutureState::ReadyForCompletion)
Definition: future.h:230
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
void Reset()
Unlists the FutureCore and resets it to the empty state.
Definition: future.h:290
constexpr FutureCore(FutureState::Pending)
Definition: future.h:225
bool in_list() const
Definition: future.h:319
Definition: future.h:111
constexpr bool is_pendable() const
Definition: future.h:156
constexpr FutureState(ReadyForCompletion)
Definition: future.h:130
constexpr FutureState(FutureState &&other)
Definition: future.h:137
Pending
Tag for constructing an active future, for which Pend may be called.
Definition: future.h:114
constexpr FutureState()
Definition: future.h:122
constexpr bool is_ready() const
Definition: future.h:169
constexpr FutureState(Pending)
Represents an active future, for which Pend may be called.
Definition: future.h:125
ReadyForCompletion
Definition: future.h:118
constexpr bool is_complete() const
Definition: future.h:162
constexpr bool is_initialized() const
Definition: future.h:175
Definition: waker.h:158
Definition: future.h:47
std::conditional_t< std::is_void_v< typename T::value_type >, ReadyType, typename T::value_type > FutureValue
Definition: future.h:107
#define PW_ASYNC_STORE_WAKER(context, waker_or_queue_out, wait_reason_string)
Definition: waker.h:58
void PushBackSlow(IntrusiveForwardList< T > &forward_list, T &item)
Inserts an element at the end of the forward list. Runs in O(n) time.
Definition: intrusive_forward_list.h:50
#define PW_NO_LOCK_SAFETY_ANALYSIS
Definition: lock_annotations.h:292
Definition: poll.h:40