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() : pend_state_(PendState::kEmpty) {}
123
125 constexpr FutureState(Pending) : pend_state_(PendState::kPending) {}
126
131 : pend_state_(PendState::kPending), ready_(true) {}
132
133 constexpr FutureState(const FutureState&) = delete;
134 constexpr FutureState& operator=(const FutureState&) = delete;
135
138 constexpr FutureState(FutureState&& other)
139 : pend_state_(cpp20::exchange(other.pend_state_, PendState::kEmpty)),
140 ready_(cpp20::exchange(other.ready_, false)) {}
141
142 constexpr FutureState& operator=(FutureState&& other) {
143 pend_state_ = cpp20::exchange(other.pend_state_, PendState::kEmpty);
144 ready_ = cpp20::exchange(other.ready_, false);
145 return *this;
146 }
147
149 [[nodiscard]] constexpr bool is_pendable() const {
150 return pend_state_ == PendState::kPending;
151 }
152
154 [[nodiscard]] constexpr bool is_complete() const {
155 return pend_state_ == PendState::kComplete;
156 }
157
163 [[nodiscard]] constexpr bool is_ready() const { return ready_; }
164
167 [[nodiscard]] constexpr bool is_initialized() const {
168 return pend_state_ != PendState::kEmpty;
169 }
170
177 void MarkReady() { ready_ = true; }
178
183 void MarkComplete() { pend_state_ = PendState::kComplete; }
184
185 private:
186 // The future's pend_state_ is exclusively managed by the task that creates
187 // and pends the future.
188 enum class PendState : unsigned char {
189 // The future is in a default constructed, empty state. It does not
190 // represent an async operation and `Pend()` cannot be called.
191 kEmpty,
192
193 // `Pend()` may be called to advance the operation represented by this
194 // future. `Pend()` may return `Ready()` or `Pending()`.
195 kPending,
196
197 // A previous call to `Pend()` returned `Ready()`.
198 kComplete,
199 } pend_state_;
200
201 // Indicates that a future was put in a ready-to-complete state.
202 //
203 // The ready flag is only set by code that wakes or advances a future,
204 // typically outside of the task that pends it.
205 bool ready_ = false;
206};
207
219class FutureCore : public IntrusiveForwardList<FutureCore>::Item {
220 public:
221 constexpr FutureCore() = default;
222
223 FutureCore(FutureCore&& other) noexcept;
224
225 FutureCore& operator=(FutureCore&& other) noexcept;
226
233 : state_(FutureState::kPending) {}
234
238 : state_(FutureState::kReadyForCompletion) {}
239
240 FutureCore(const FutureCore&) = delete;
241 FutureCore& operator=(const FutureCore&) = delete;
242
243 ~FutureCore() = default;
244
246 [[nodiscard]] constexpr bool is_pendable() const {
247 return state_.is_pendable();
248 }
249
251 [[nodiscard]] constexpr bool is_complete() const {
252 return state_.is_complete();
253 }
254
261 [[nodiscard]] constexpr bool is_ready() const { return state_.is_ready(); }
262
264 [[nodiscard]] constexpr bool is_initialized() const {
265 return state_.is_initialized();
266 }
267
270 void Wake() { waker_.Wake(); }
271
278 // Mark ready before waking to guarantee so that task will run with the
279 // ready future (though `is_ready()` should only be accessed in `Pend()`
280 // with a lock held anyway).
281 state_.MarkReady();
282 Wake();
283 }
284
290 Waker& waker() { return waker_; }
291
297 void MarkComplete() { state_.MarkComplete(); }
298
300 void Unlist() { unlist(); }
301
303 void Reset() {
304 Unlist();
305 state_ = FutureState();
306 }
307
317 template <typename FutureType>
318 auto DoPend(FutureType& future, Context& cx) PW_NO_LOCK_SAFETY_ANALYSIS {
319 PW_ASSERT(is_pendable());
320
321 auto poll = future.DoPend(cx);
322 if (poll.IsPending()) {
323 PW_ASYNC_STORE_WAKER(cx, waker_, FutureType::kWaitReason);
324 } else {
325 MarkComplete();
326 }
327
328 return poll;
329 }
330
332 [[nodiscard]] bool in_list() const { return !unlisted(); }
333
334 private:
335 friend class BaseFutureList; // for IntrusiveForwardList::Item functions
336
337 Waker waker_;
338 FutureState state_;
339};
340
349 public:
350 constexpr BaseFutureList() = default;
351
352 bool empty() const { return list_.empty(); }
353
354 void Push(FutureCore& future) { containers::PushBackSlow(list_, future); }
355
356 void PushRequireEmpty(FutureCore& future);
357
358 bool PushIfEmpty(FutureCore& future);
359
360 FutureCore* PopIfAvailable() { return list_.empty() ? nullptr : &Pop(); }
361
362 FutureCore& Pop() {
363 FutureCore& future = list_.front();
364 list_.pop_front();
365 return future;
366 }
367
370 void ResolveOne() {
371 list_.front().WakeAndMarkReady();
372 list_.pop_front();
373 }
374
375 void ResolveOneIfAvailable() {
376 if (!list_.empty()) {
377 ResolveOne();
378 }
379 }
380
383
384 protected:
385 IntrusiveForwardList<FutureCore>& list() { return list_; }
386
387 private:
389};
390
398template <auto kGetFutureImpl, auto kGetFutureCore>
400 public:
401 using value_type = std::remove_reference_t<decltype(*kGetFutureImpl(
402 std::declval<FutureCore*>()))>;
403 using pointer = value_type*;
404 using reference = value_type&;
405
406 constexpr CustomFutureList() = default;
407
408 void Push(FutureCore& future) { BaseFutureList::Push(future); }
409 void Push(reference future) { Push(kGetFutureCore(future)); }
410
411 void PushRequireEmpty(FutureCore& future) {
412 BaseFutureList::PushRequireEmpty(future);
413 }
414 void PushRequireEmpty(reference future) {
415 PushRequireEmpty(kGetFutureCore(future));
416 }
417
418 bool PushIfEmpty(FutureCore& future) {
419 return BaseFutureList::PushIfEmpty(future);
420 }
421 bool PushIfEmpty(reference future) {
422 return PushIfEmpty(kGetFutureCore(future));
423 }
424
425 pointer PopIfAvailable() {
426 return kGetFutureImpl(BaseFutureList::PopIfAvailable());
427 }
428
429 reference Pop() { return *kGetFutureImpl(BaseFutureList::PopIfAvailable()); }
430
435 template <typename Predicate>
436 pointer Remove(Predicate predicate) {
437 auto previous = list().before_begin();
438 auto current = list().begin();
439 while (current != list().end()) {
440 pointer item = kGetFutureImpl(&(*current));
441 if (predicate(*item)) {
442 list().erase_after(previous);
443 return item;
444 }
445 previous = current;
446 ++current;
447 }
448 return nullptr;
449 }
450
451 template <typename Resolver>
452 void ResolveAllWith(Resolver&& resolver) {
453 while (!list().empty()) {
454 resolver(Pop());
455 }
456 }
457
458 template <typename Resolver>
459 void ResolveOneWith(Resolver&& resolver) {
460 if (!list().empty()) {
461 resolver(Pop());
462 }
463 }
464};
465
470template <auto kMemberPtr>
473
475
476} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: future.h:348
void ResolveOne()
Definition: future.h:370
void ResolveAll()
Pops all futures and calls WakeAndMarkReady() on them.
Definition: context.h:46
Definition: future.h:399
pointer Remove(Predicate predicate)
Definition: future.h:436
Definition: future.h:219
void WakeAndMarkReady()
Definition: future.h:277
constexpr bool is_initialized() const
Definition: future.h:264
Waker & waker()
Definition: future.h:290
void Wake()
Definition: future.h:270
constexpr bool is_pendable() const
Definition: future.h:246
void MarkComplete()
Definition: future.h:297
constexpr FutureCore(FutureState::ReadyForCompletion)
Definition: future.h:237
constexpr bool is_complete() const
Definition: future.h:251
auto DoPend(FutureType &future, Context &cx)
Definition: future.h:318
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:300
constexpr bool is_ready() const
Definition: future.h:261
void Reset()
Unlists the FutureCore and resets it to the empty state.
Definition: future.h:303
constexpr FutureCore(FutureState::Pending)
Definition: future.h:232
bool in_list() const
Definition: future.h:332
Definition: future.h:111
constexpr bool is_pendable() const
Definition: future.h:149
constexpr FutureState(ReadyForCompletion)
Definition: future.h:130
constexpr FutureState(FutureState &&other)
Definition: future.h:138
Pending
Tag for constructing an active future, for which Pend may be called.
Definition: future.h:114
constexpr FutureState()
Definition: future.h:122
void MarkComplete()
Definition: future.h:183
constexpr bool is_ready() const
Definition: future.h:163
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:154
void MarkReady()
Definition: future.h:177
constexpr bool is_initialized() const
Definition: future.h:167
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