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
65 static_cast<Poll<typename T::value_type> (T::*)(Context&)>(&T::Pend);
66
69} && std::default_initializable<T> && std::destructible<T> && std::movable<T>;
70
71#else // C++17 version
72
73namespace internal {
74
75template <typename T, typename = void>
76struct is_future : std::false_type {};
77
78template <typename T>
79struct is_future<
80 T,
81 std::void_t<typename T::value_type,
82 decltype(std::declval<T&>().Pend(std::declval<Context&>())),
83 decltype(std::declval<const T&>().is_pendable()),
84 decltype(std::declval<const T&>().is_complete())>>
85 : std::conjunction<
86 std::is_convertible<decltype(&T::is_pendable), bool (T::*)() const>,
87 std::is_convertible<decltype(&T::is_complete), bool (T::*)() const>,
88 std::is_convertible<decltype(&T::Pend),
89 Poll<typename T::value_type> (T::*)(Context&)>,
90 std::is_default_constructible<T>,
91 std::is_destructible<T>> {};
92
93} // namespace internal
94
95// This variable is named as a type to match the C++20 concept. This makes it
96// possible to use `Future` in static asserts in either C++17 or C++20.
97template <typename T>
98constexpr bool Future =
99 internal::is_future<std::remove_cv_t<std::remove_reference_t<T>>>::value;
100
101#endif // __cpp_concepts
102
106template <typename T>
107using FutureValue = std::conditional_t<std::is_void_v<typename T::value_type>,
108 ReadyType,
109 typename T::value_type>;
110
114 public:
116 enum Pending { kPending };
117
120 enum ReadyForCompletion { kReadyForCompletion };
121
124 constexpr FutureState() : pend_state_(PendState::kEmpty) {}
125
127 constexpr FutureState(Pending) : pend_state_(PendState::kPending) {}
128
133 : pend_state_(PendState::kPending), ready_(true) {}
134
135 constexpr FutureState(const FutureState&) = delete;
136 constexpr FutureState& operator=(const FutureState&) = delete;
137
140 constexpr FutureState(FutureState&& other)
141 : pend_state_(cpp20::exchange(other.pend_state_, PendState::kEmpty)),
142 ready_(cpp20::exchange(other.ready_, false)) {}
143
144 constexpr FutureState& operator=(FutureState&& other) {
145 pend_state_ = cpp20::exchange(other.pend_state_, PendState::kEmpty);
146 ready_ = cpp20::exchange(other.ready_, false);
147 return *this;
148 }
149
151 [[nodiscard]] constexpr bool is_pendable() const {
152 return pend_state_ == PendState::kPending;
153 }
154
156 [[nodiscard]] constexpr bool is_complete() const {
157 return pend_state_ == PendState::kComplete;
158 }
159
165 [[nodiscard]] constexpr bool is_ready() const { return ready_; }
166
169 [[nodiscard]] constexpr bool is_initialized() const {
170 return pend_state_ != PendState::kEmpty;
171 }
172
179 void MarkReady() { ready_ = true; }
180
185 void MarkComplete() { pend_state_ = PendState::kComplete; }
186
187 private:
188 // The future's pend_state_ is exclusively managed by the task that creates
189 // and pends the future.
190 enum class PendState : unsigned char {
191 // The future is in a default constructed, empty state. It does not
192 // represent an async operation and `Pend()` cannot be called.
193 kEmpty,
194
195 // `Pend()` may be called to advance the operation represented by this
196 // future. `Pend()` may return `Ready()` or `Pending()`.
197 kPending,
198
199 // A previous call to `Pend()` returned `Ready()`.
200 kComplete,
201 } pend_state_;
202
203 // Indicates that a future was put in a ready-to-complete state.
204 //
205 // The ready flag is only set by code that wakes or advances a future,
206 // typically outside of the task that pends it.
207 bool ready_ = false;
208};
209
221class FutureCore : public IntrusiveForwardList<FutureCore>::Item {
222 public:
223 constexpr FutureCore() = default;
224
225 FutureCore(FutureCore&& other) noexcept;
226
227 FutureCore& operator=(FutureCore&& other) noexcept;
228
235 : state_(FutureState::kPending) {}
236
240 : state_(FutureState::kReadyForCompletion) {}
241
242 FutureCore(const FutureCore&) = delete;
243 FutureCore& operator=(const FutureCore&) = delete;
244
245 ~FutureCore() = default;
246
248 [[nodiscard]] constexpr bool is_pendable() const {
249 return state_.is_pendable();
250 }
251
253 [[nodiscard]] constexpr bool is_complete() const {
254 return state_.is_complete();
255 }
256
263 [[nodiscard]] constexpr bool is_ready() const { return state_.is_ready(); }
264
266 [[nodiscard]] constexpr bool is_initialized() const {
267 return state_.is_initialized();
268 }
269
272 void Wake() { waker_.Wake(); }
273
280 // Mark ready before waking to guarantee so that task will run with the
281 // ready future (though `is_ready()` should only be accessed in `Pend()`
282 // with a lock held anyway).
283 state_.MarkReady();
284 Wake();
285 }
286
292 Waker& waker() { return waker_; }
293
299 void MarkComplete() { state_.MarkComplete(); }
300
302 void Unlist() { unlist(); }
303
305 void Reset() {
306 Unlist();
307 state_ = FutureState();
308 }
309
319 template <typename FutureType>
320 auto DoPend(FutureType& future, Context& cx) PW_NO_LOCK_SAFETY_ANALYSIS {
321 PW_ASSERT(is_pendable());
322
323 auto poll = future.DoPend(cx);
324 if (poll.IsPending()) {
325 PW_ASYNC_STORE_WAKER(cx, waker_, FutureType::kWaitReason);
326 } else {
327 MarkComplete();
328 }
329
330 return poll;
331 }
332
334 [[nodiscard]] bool in_list() const { return !unlisted(); }
335
336 private:
337 friend class BaseFutureList; // for IntrusiveForwardList::Item functions
338
339 Waker waker_;
340 FutureState state_;
341};
342
351 public:
352 constexpr BaseFutureList() = default;
353
354 bool empty() const { return list_.empty(); }
355
356 void Push(FutureCore& future) { containers::PushBackSlow(list_, future); }
357
358 void PushRequireEmpty(FutureCore& future);
359
360 bool PushIfEmpty(FutureCore& future);
361
362 FutureCore* PopIfAvailable() { return list_.empty() ? nullptr : &Pop(); }
363
364 FutureCore& Pop() {
365 FutureCore& future = list_.front();
366 list_.pop_front();
367 return future;
368 }
369
372 void ResolveOne() {
373 list_.front().WakeAndMarkReady();
374 list_.pop_front();
375 }
376
377 void ResolveOneIfAvailable() {
378 if (!list_.empty()) {
379 ResolveOne();
380 }
381 }
382
385
386 protected:
387 IntrusiveForwardList<FutureCore>& list() { return list_; }
388
389 private:
391};
392
400template <auto kGetFutureImpl, auto kGetFutureCore>
402 public:
403 using value_type = std::remove_reference_t<decltype(*kGetFutureImpl(
404 std::declval<FutureCore*>()))>;
405 using pointer = value_type*;
406 using reference = value_type&;
407
408 constexpr CustomFutureList() = default;
409
410 void Push(FutureCore& future) { BaseFutureList::Push(future); }
411 void Push(reference future) { Push(kGetFutureCore(future)); }
412
413 void PushRequireEmpty(FutureCore& future) {
414 BaseFutureList::PushRequireEmpty(future);
415 }
416 void PushRequireEmpty(reference future) {
417 PushRequireEmpty(kGetFutureCore(future));
418 }
419
420 bool PushIfEmpty(FutureCore& future) {
421 return BaseFutureList::PushIfEmpty(future);
422 }
423 bool PushIfEmpty(reference future) {
424 return PushIfEmpty(kGetFutureCore(future));
425 }
426
427 pointer PopIfAvailable() {
428 return kGetFutureImpl(BaseFutureList::PopIfAvailable());
429 }
430
431 reference Pop() { return *kGetFutureImpl(BaseFutureList::PopIfAvailable()); }
432
437 template <typename Predicate>
438 pointer Remove(Predicate predicate) {
439 auto previous = list().before_begin();
440 auto current = list().begin();
441 while (current != list().end()) {
442 pointer item = kGetFutureImpl(&(*current));
443 if (predicate(*item)) {
444 list().erase_after(previous);
445 return item;
446 }
447 previous = current;
448 ++current;
449 }
450 return nullptr;
451 }
452
453 template <typename Resolver>
454 void ResolveAllWith(Resolver&& resolver) {
455 while (!list().empty()) {
456 resolver(Pop());
457 }
458 }
459
460 template <typename Resolver>
461 void ResolveOneWith(Resolver&& resolver) {
462 if (!list().empty()) {
463 resolver(Pop());
464 }
465 }
466};
467
472template <auto kMemberPtr>
475
477
478} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: future.h:350
void ResolveOne()
Definition: future.h:372
void ResolveAll()
Pops all futures and calls WakeAndMarkReady() on them.
Definition: context.h:46
Definition: future.h:401
pointer Remove(Predicate predicate)
Definition: future.h:438
Definition: future.h:221
void WakeAndMarkReady()
Definition: future.h:279
constexpr bool is_initialized() const
Definition: future.h:266
Waker & waker()
Definition: future.h:292
void Wake()
Definition: future.h:272
constexpr bool is_pendable() const
Definition: future.h:248
void MarkComplete()
Definition: future.h:299
constexpr FutureCore(FutureState::ReadyForCompletion)
Definition: future.h:239
constexpr bool is_complete() const
Definition: future.h:253
auto DoPend(FutureType &future, Context &cx)
Definition: future.h:320
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:302
constexpr bool is_ready() const
Definition: future.h:263
void Reset()
Unlists the FutureCore and resets it to the empty state.
Definition: future.h:305
constexpr FutureCore(FutureState::Pending)
Definition: future.h:234
bool in_list() const
Definition: future.h:334
Definition: future.h:113
constexpr bool is_pendable() const
Definition: future.h:151
constexpr FutureState(ReadyForCompletion)
Definition: future.h:132
constexpr FutureState(FutureState &&other)
Definition: future.h:140
Pending
Tag for constructing an active future, for which Pend may be called.
Definition: future.h:116
constexpr FutureState()
Definition: future.h:124
void MarkComplete()
Definition: future.h:185
constexpr bool is_ready() const
Definition: future.h:165
constexpr FutureState(Pending)
Represents an active future, for which Pend may be called.
Definition: future.h:127
ReadyForCompletion
Definition: future.h:120
constexpr bool is_complete() const
Definition: future.h:156
void MarkReady()
Definition: future.h:179
constexpr bool is_initialized() const
Definition: future.h:169
Definition: waker.h:159
Definition: future.h:47
std::conditional_t< std::is_void_v< typename T::value_type >, ReadyType, typename T::value_type > FutureValue
Definition: future.h:109
#define PW_ASYNC_STORE_WAKER(context, waker_or_queue_out, wait_reason_string)
Definition: waker.h:60
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