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/poll.h"
27#include "pw_async2/task.h"
28#include "pw_async2/waker.h"
29#include "pw_containers/intrusive_list.h"
30#include "pw_memory/container_of.h"
31#include "pw_sync/interrupt_spin_lock.h"
32
33namespace pw::async2 {
34
36
37#ifdef __cpp_concepts
38
47template <typename T>
48concept Future = requires {
52 typename T::value_type;
53
55 static_cast<bool (T::*)() const>(&T::is_pendable);
56
58 static_cast<bool (T::*)() const>(&T::is_complete);
59
66 static_cast<Poll<typename T::value_type> (T::*)(Context&)>(&T::Pend);
67
70} && std::default_initializable<T> && std::destructible<T> && std::movable<T>;
71
72#else // C++17 version
73
74namespace internal {
75
76template <typename T, typename = void>
77struct is_future : std::false_type {};
78
79template <typename T>
80struct is_future<
81 T,
82 std::void_t<typename T::value_type,
83 decltype(std::declval<T&>().Pend(std::declval<Context&>())),
84 decltype(std::declval<const T&>().is_pendable()),
85 decltype(std::declval<const T&>().is_complete())>>
86 : std::conjunction<
87 std::is_convertible<decltype(&T::is_pendable), bool (T::*)() const>,
88 std::is_convertible<decltype(&T::is_complete), bool (T::*)() const>,
89 std::is_convertible<decltype(&T::Pend),
90 Poll<typename T::value_type> (T::*)(Context&)>,
91 std::is_default_constructible<T>,
92 std::is_destructible<T>> {};
93
94} // namespace internal
95
96// This variable is named as a type to match the C++20 concept. This makes it
97// possible to use `Future` in static asserts in either C++17 or C++20.
98template <typename T>
99constexpr bool Future =
100 internal::is_future<std::remove_cv_t<std::remove_reference_t<T>>>::value;
101
102#endif // __cpp_concepts
103
107template <typename T>
108using FutureValue = std::conditional_t<std::is_void_v<typename T::value_type>,
109 ReadyType,
110 typename T::value_type>;
111
115 public:
117 enum Pending { kPending };
118
121 enum ReadyForCompletion { kReadyForCompletion };
122
125 constexpr FutureState() : pend_state_(PendState::kEmpty) {}
126
128 constexpr FutureState(Pending) : pend_state_(PendState::kPending) {}
129
134 : pend_state_(PendState::kPending), ready_(true) {}
135
136 constexpr FutureState(const FutureState&) = delete;
137 constexpr FutureState& operator=(const FutureState&) = delete;
138
141 constexpr FutureState(FutureState&& other)
142 : pend_state_(cpp20::exchange(other.pend_state_, PendState::kEmpty)),
143 ready_(cpp20::exchange(other.ready_, false)) {}
144
145 constexpr FutureState& operator=(FutureState&& other) {
146 pend_state_ = cpp20::exchange(other.pend_state_, PendState::kEmpty);
147 ready_ = cpp20::exchange(other.ready_, false);
148 return *this;
149 }
150
152 [[nodiscard]] constexpr bool is_pendable() const {
153 return pend_state_ == PendState::kPending;
154 }
155
157 [[nodiscard]] constexpr bool is_complete() const {
158 return pend_state_ == PendState::kComplete;
159 }
160
166 [[nodiscard]] constexpr bool is_ready() const { return ready_; }
167
170 [[nodiscard]] constexpr bool is_initialized() const {
171 return pend_state_ != PendState::kEmpty;
172 }
173
180 void MarkReady() { ready_ = true; }
181
186 void MarkComplete() { pend_state_ = PendState::kComplete; }
187
188 private:
189 // The future's pend_state_ is exclusively managed by the task that creates
190 // and pends the future.
191 enum class PendState : unsigned char {
192 // The future is in a default constructed, empty state. It does not
193 // represent an async operation and `Pend()` cannot be called.
194 kEmpty,
195
196 // `Pend()` may be called to advance the operation represented by this
197 // future. `Pend()` may return `Ready()` or `Pending()`.
198 kPending,
199
200 // A previous call to `Pend()` returned `Ready()`.
201 kComplete,
202 } pend_state_;
203
204 // Indicates that a future was put in a ready-to-complete state.
205 //
206 // The ready flag is only set by code that wakes or advances a future,
207 // typically outside of the task that pends it.
208 bool ready_ = false;
209};
210
228class FutureCore : public IntrusiveForwardList<FutureCore>::Item {
229 public:
230 constexpr FutureCore() = default;
231
232 FutureCore(FutureCore&& other) noexcept;
233
234 FutureCore& operator=(FutureCore&& other) noexcept;
235
242 : state_(FutureState::kPending) {}
243
247 : state_(FutureState::kReadyForCompletion) {}
248
249 FutureCore(const FutureCore&) = delete;
250 FutureCore& operator=(const FutureCore&) = delete;
251
252 ~FutureCore() = default;
253
255 [[nodiscard]] constexpr bool is_pendable() const {
256 return state_.is_pendable();
257 }
258
260 [[nodiscard]] constexpr bool is_complete() const {
261 return state_.is_complete();
262 }
263
270 [[nodiscard]] constexpr bool is_ready() const { return state_.is_ready(); }
271
273 [[nodiscard]] constexpr bool is_initialized() const {
274 return state_.is_initialized();
275 }
276
279 void Wake() { waker_.Wake(); }
280
287 // Mark ready before waking to guarantee so that task will run with the
288 // ready future (though `is_ready()` should only be accessed in `Pend()`
289 // with a lock held anyway).
290 state_.MarkReady();
291 Wake();
292 }
293
299 Waker& waker() { return waker_; }
300
306 void MarkComplete() { state_.MarkComplete(); }
307
309 void Unlist() { unlist(); }
310
312 void Reset() {
313 Unlist();
314 state_ = FutureState();
315 }
316
326 template <typename FutureType>
327 auto DoPend(FutureType& future, Context& cx) PW_NO_LOCK_SAFETY_ANALYSIS {
328 PW_ASSERT(is_pendable());
329
330 auto poll = future.DoPend(cx);
331 if (poll.IsPending()) {
332 PW_ASYNC_STORE_WAKER(cx, waker_, FutureType::kWaitReason);
333 } else {
334 MarkComplete();
335 }
336
337 return poll;
338 }
339
341 [[nodiscard]] bool in_list() const { return !unlisted(); }
342
343 private:
344 friend class BaseFutureList; // for IntrusiveForwardList::Item functions
345
346 Waker waker_;
347 FutureState state_;
348};
349
358 public:
359 constexpr BaseFutureList() = default;
360
361 bool empty() const { return list_.empty(); }
362
363 void Push(FutureCore& future) { containers::PushBackSlow(list_, future); }
364
365 void PushRequireEmpty(FutureCore& future);
366
367 bool PushIfEmpty(FutureCore& future);
368
369 FutureCore* PopIfAvailable() { return list_.empty() ? nullptr : &Pop(); }
370
371 FutureCore& Pop() {
372 FutureCore& future = list_.front();
373 list_.pop_front();
374 return future;
375 }
376
379 void ResolveOne() {
380 list_.front().WakeAndMarkReady();
381 list_.pop_front();
382 }
383
384 void ResolveOneIfAvailable() {
385 if (!list_.empty()) {
386 ResolveOne();
387 }
388 }
389
392
393 protected:
394 IntrusiveForwardList<FutureCore>& list() { return list_; }
395
396 private:
398};
399
407template <auto kGetFutureImpl, auto kGetFutureCore>
409 public:
410 using value_type = std::remove_reference_t<decltype(*kGetFutureImpl(
411 std::declval<FutureCore*>()))>;
412 using pointer = value_type*;
413 using reference = value_type&;
414
415 constexpr CustomFutureList() = default;
416
417 void Push(FutureCore& future) { BaseFutureList::Push(future); }
418 void Push(reference future) { Push(kGetFutureCore(future)); }
419
420 void PushRequireEmpty(FutureCore& future) {
421 BaseFutureList::PushRequireEmpty(future);
422 }
423 void PushRequireEmpty(reference future) {
424 PushRequireEmpty(kGetFutureCore(future));
425 }
426
427 bool PushIfEmpty(FutureCore& future) {
428 return BaseFutureList::PushIfEmpty(future);
429 }
430 bool PushIfEmpty(reference future) {
431 return PushIfEmpty(kGetFutureCore(future));
432 }
433
434 pointer PopIfAvailable() {
435 return kGetFutureImpl(BaseFutureList::PopIfAvailable());
436 }
437
438 reference Pop() { return *kGetFutureImpl(BaseFutureList::PopIfAvailable()); }
439
444 template <typename Predicate>
445 pointer Remove(Predicate predicate) {
446 auto previous = list().before_begin();
447 auto current = list().begin();
448 while (current != list().end()) {
449 pointer item = kGetFutureImpl(&(*current));
450 if (predicate(*item)) {
451 list().erase_after(previous);
452 return item;
453 }
454 previous = current;
455 ++current;
456 }
457 return nullptr;
458 }
459
460 template <typename Resolver>
461 void ResolveAllWith(Resolver&& resolver) {
462 while (!list().empty()) {
463 resolver(Pop());
464 }
465 }
466
467 template <typename Resolver>
468 void ResolveOneWith(Resolver&& resolver) {
469 if (!list().empty()) {
470 resolver(Pop());
471 }
472 }
473};
474
479template <auto kMemberPtr>
482
484
485} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: future.h:357
void ResolveOne()
Definition: future.h:379
void ResolveAll()
Pops all futures and calls WakeAndMarkReady() on them.
Definition: task.h:45
Definition: future.h:408
pointer Remove(Predicate predicate)
Definition: future.h:445
Definition: future.h:228
void WakeAndMarkReady()
Definition: future.h:286
constexpr bool is_initialized() const
Definition: future.h:273
Waker & waker()
Definition: future.h:299
void Wake()
Definition: future.h:279
constexpr bool is_pendable() const
Definition: future.h:255
void MarkComplete()
Definition: future.h:306
constexpr FutureCore(FutureState::ReadyForCompletion)
Definition: future.h:246
constexpr bool is_complete() const
Definition: future.h:260
auto DoPend(FutureType &future, Context &cx)
Definition: future.h:327
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:309
constexpr bool is_ready() const
Definition: future.h:270
void Reset()
Unlists the FutureCore and resets it to the empty state.
Definition: future.h:312
constexpr FutureCore(FutureState::Pending)
Definition: future.h:241
bool in_list() const
Definition: future.h:341
Definition: future.h:114
constexpr bool is_pendable() const
Definition: future.h:152
constexpr FutureState(ReadyForCompletion)
Definition: future.h:133
constexpr FutureState(FutureState &&other)
Definition: future.h:141
Pending
Tag for constructing an active future, for which Pend may be called.
Definition: future.h:117
constexpr FutureState()
Definition: future.h:125
void MarkComplete()
Definition: future.h:186
constexpr bool is_ready() const
Definition: future.h:166
constexpr FutureState(Pending)
Represents an active future, for which Pend may be called.
Definition: future.h:128
ReadyForCompletion
Definition: future.h:121
constexpr bool is_complete() const
Definition: future.h:157
void MarkReady()
Definition: future.h:180
constexpr bool is_initialized() const
Definition: future.h:170
Definition: waker.h:155
Definition: future.h:48
std::conditional_t< std::is_void_v< typename T::value_type >, ReadyType, typename T::value_type > FutureValue
Definition: future.h:110
#define PW_ASYNC_STORE_WAKER(context, waker_out, wait_reason_string)
Definition: waker.h:64
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:296
Definition: poll.h:40