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#include <mutex>
17#include <optional>
18#include <type_traits>
19
20#include "pw_assert/assert.h"
21#include "pw_async2/context.h"
22#include "pw_async2/poll.h"
23#include "pw_containers/intrusive_list.h"
24#include "pw_memory/container_of.h"
25#include "pw_sync/interrupt_spin_lock.h"
26
27namespace pw::async2 {
28
30
64template <typename Derived, typename T>
65class Future {
66 public:
67 using value_type = std::conditional_t<std::is_void_v<T>, ReadyType, T>;
68
77 PW_ASSERT(!is_complete());
78 Poll<value_type> poll = derived().DoPend(cx);
79 if (poll.IsReady()) {
80 derived().DoMarkComplete();
81 }
82 return poll;
83 }
84
88 bool is_complete() const { return derived().DoIsComplete(); }
89
90 protected:
91 constexpr Future() = default;
92
93 private:
94 Derived& derived() { return static_cast<Derived&>(*this); }
95 const Derived& derived() const { return static_cast<const Derived&>(*this); }
96};
97
98template <typename T, typename = void>
99struct is_future : std::false_type {};
100
101template <typename T>
103 T,
104 std::void_t<typename T::value_type,
105 decltype(std::declval<T&>().Pend(std::declval<Context&>())),
106 decltype(std::declval<const T&>().is_complete())>>
107 : std::is_convertible<decltype(&T::Pend),
108 Poll<typename T::value_type> (T::*)(Context&)> {};
109
110template <typename T>
111constexpr bool is_future_v =
113
137template <typename FutureType, typename Lock = sync::InterruptSpinLock>
139 public:
140 constexpr ListFutureProvider() = default;
141
142 ListFutureProvider(const ListFutureProvider&) = delete;
143 ListFutureProvider& operator=(const ListFutureProvider&) = delete;
144
146 void Push(FutureType& future) {
147 std::lock_guard lock(lock_);
148 futures_.push_back(future);
149 }
150
152 std::optional<std::reference_wrapper<FutureType>> Pop() {
153 std::lock_guard lock(lock_);
154 if (futures_.empty()) {
155 return std::nullopt;
156 }
157 FutureType& future = futures_.front();
158 futures_.pop_front();
159 return std::ref(future);
160 }
161
163 bool empty() {
164 std::lock_guard lock(lock_);
165 return futures_.empty();
166 }
167
169 Lock& lock() { return lock_; }
170
171 private:
172 friend FutureType;
173
174 template <typename, typename>
175 friend class ListableFutureWithWaker;
176
177 using LockType = Lock;
178
180 Lock lock_;
181};
182
194template <typename FutureType>
196 public:
197 constexpr SingleFutureProvider() = default;
198
200 SingleFutureProvider& operator=(const SingleFutureProvider&) = delete;
201
203 void Set(FutureType& future) {
204 PW_ASSERT(!has_future());
205 inner_.Push(future);
206 }
207
209 bool TrySet(FutureType& future) {
210 if (has_future()) {
211 return false;
212 }
213 inner_.Push(future);
214 return true;
215 }
216
218 [[nodiscard]] std::optional<std::reference_wrapper<FutureType>> Take() {
219 return inner_.Pop();
220 }
221
223 bool has_future() { return !inner_.empty(); }
224
225 private:
226 template <typename, typename>
227 friend class ListableFutureWithWaker;
228 friend FutureType;
229
231};
232
283template <typename Derived, typename T>
285 : public Future<ListableFutureWithWaker<Derived, T>, T>,
286 public IntrusiveList<Derived>::Item {
287 public:
289 ListableFutureWithWaker& operator=(const ListableFutureWithWaker&) = delete;
290
291 protected:
294 class PW_LOCKABLE("pw::async2::ListableFutureWithWaker::Lock") Provider {
295 public:
296 void lock() PW_EXCLUSIVE_LOCK_FUNCTION() {
297 if (provider_ != nullptr) {
298 provider_->lock().lock();
299 }
300 }
301
302 void unlock() PW_UNLOCK_FUNCTION() {
303 if (provider_ != nullptr) {
304 provider_->lock().unlock();
305 }
306 }
307
308 Provider& operator=(ListFutureProvider<Derived>* provider) {
309 provider_ = provider;
310 return *this;
311 }
312
313 ListFutureProvider<Derived>* get() const { return provider_; }
314 ListFutureProvider<Derived>& operator*() const {
315 PW_ASSERT(provider_ != nullptr);
316 return *provider_;
317 }
318 ListFutureProvider<Derived>* operator->() const {
319 PW_ASSERT(provider_ != nullptr);
320 return provider_;
321 }
322
323 explicit operator bool() const { return provider_ != nullptr; }
324
325 private:
326 friend class ListableFutureWithWaker<Derived, T>;
327
328 explicit Provider(ListFutureProvider<Derived>* provider)
329 : provider_(provider) {}
330
332 };
333
334 using Lock = Provider;
335
337 enum ConstructedState { kMovedFrom, kReadyForCompletion };
338
352 : provider_(nullptr), complete_(state == kMovedFrom) {}
353
355 : provider_(&provider) {
356 provider.Push(derived());
357 }
358 explicit ListableFutureWithWaker(SingleFutureProvider<Derived>& single)
359 : ListableFutureWithWaker(single.inner_) {}
360
361 ~ListableFutureWithWaker() {
362 if (!provider_) {
363 return;
364 }
365 std::lock_guard guard(lock());
366 if (!this->unlisted()) {
367 this->unlist();
368 }
369 }
370
371 void MoveFrom(ListableFutureWithWaker& other) {
372 complete_ = std::exchange(other.complete_, true);
373 provider_ = std::exchange(other.provider_, nullptr);
374 waker_ = std::move(other.waker_);
375
376 if (provider_) {
377 std::lock_guard guard(lock());
378 if (!other.unlisted()) {
379 this->replace(other);
380 }
381 }
382 }
383
384 ListFutureProvider<Derived>& provider() { return *provider_; }
385
386 Lock& lock() { return provider_; }
387
389 void Wake() { waker_.Wake(); }
390
391 private:
393
394 friend Base;
396
398 static_assert(
399 std::is_same_v<std::remove_extent_t<decltype(Derived::kWaitReason)>,
400 const char>,
401 "kWaitReason must be a character array");
402
403 Poll<typename Base::value_type> poll = derived().DoPend(cx);
404 if (poll.IsPending()) {
405 PW_ASYNC_STORE_WAKER(cx, waker_, Derived::kWaitReason);
406 Relist();
407 }
408 return poll;
409 }
410
412 void Relist() {
413 std::lock_guard guard(lock());
414 if (provider_ && this->unlisted()) {
415 provider_->futures_.push_back(derived());
416 }
417 }
418
419 void DoMarkComplete() { complete_ = true; }
420 bool DoIsComplete() const { return complete_; }
421
422 Derived& derived() { return static_cast<Derived&>(*this); }
423
424 Provider provider_;
425 Waker waker_;
426 bool complete_ = false;
427};
428
440class FutureCore : public IntrusiveForwardList<FutureCore>::Item {
441 public:
442 constexpr FutureCore() : state_(State::kNull) {}
443
444 FutureCore(FutureCore&& other) noexcept;
445
446 FutureCore& operator=(FutureCore&& other) noexcept;
447
450 enum ReadyForCompletion { kReadyForCompletion };
451
453 enum Pending { kPending };
454
460 explicit constexpr FutureCore(Pending) : state_(State::kPending) {}
461
464 explicit constexpr FutureCore(ReadyForCompletion) : state_(State::kReady) {}
465
466 FutureCore(const FutureCore&) = delete;
467 FutureCore& operator=(const FutureCore&) = delete;
468
469 ~FutureCore() = default;
470
472 [[nodiscard]] bool is_pendable() const { return state_ > State::kComplete; }
473
476 [[nodiscard]] bool is_complete() const { return state_ == State::kComplete; }
477
482 [[nodiscard]] bool is_ready() const { return state_ == State::kReady; }
483
486 void Wake() { waker_.Wake(); }
487
491 Wake();
492 state_ = State::kReady;
493 }
494
496 void Unlist() { unlist(); }
497
498 template <typename FutureType>
499 auto DoPend(FutureType& future, Context& cx) PW_NO_LOCK_SAFETY_ANALYSIS {
500 PW_ASSERT(is_pendable());
501
502 auto poll = future.DoPend(cx);
503 if (poll.IsPending()) {
504 PW_ASYNC_STORE_WAKER(cx, waker_, FutureType::kWaitReason);
505 } else {
506 state_ = State::kComplete;
507 }
508
509 return poll;
510 }
511
513 [[nodiscard]] bool in_list() const { return !unlisted(); }
514
515 private:
516 friend class BaseFutureList; // for IntrusiveForwardList::Item functions
517
519 [[nodiscard]] bool is_initialized() const { return state_ != State::kNull; }
520
521 Waker waker_;
522
523 enum class State : unsigned char {
526 kNull,
527
529 kComplete,
530
532 kReady,
533
536 kPending,
537 } state_;
538};
539
548 public:
549 constexpr BaseFutureList() = default;
550
551 bool empty() const { return list_.empty(); }
552
553 void Push(FutureCore& future) { containers::PushBackSlow(list_, future); }
554
555 void PushRequireEmpty(FutureCore& future);
556
557 bool PushIfEmpty(FutureCore& future);
558
559 FutureCore* PopIfAvailable() { return list_.empty() ? nullptr : &Pop(); }
560
561 FutureCore& Pop() {
562 FutureCore& future = list_.front();
563 list_.pop_front();
564 return future;
565 }
566
569 void ResolveOne() {
570 list_.front().WakeAndMarkReady();
571 list_.pop_front();
572 }
573
574 void ResolveOneIfAvailable() {
575 if (!list_.empty()) {
576 ResolveOne();
577 }
578 }
579
582
583 protected:
584 IntrusiveForwardList<FutureCore>& list() { return list_; }
585
586 private:
588};
589
597template <auto kGetFutureImpl, auto kGetFutureCore>
599 public:
600 using value_type = std::remove_reference_t<decltype(*kGetFutureImpl(
601 std::declval<FutureCore*>()))>;
602 using pointer = value_type*;
603 using reference = value_type&;
604
605 constexpr CustomFutureList() = default;
606
607 void Push(FutureCore& future) { BaseFutureList::Push(future); }
608 void Push(reference future) { Push(kGetFutureCore(future)); }
609
610 void PushRequireEmpty(FutureCore& future) {
611 BaseFutureList::PushRequireEmpty(future);
612 }
613 void PushRequireEmpty(reference future) {
614 PushRequireEmpty(kGetFutureCore(future));
615 }
616
617 bool PushIfEmpty(FutureCore& future) {
618 return BaseFutureList::PushIfEmpty(future);
619 }
620 bool PushIfEmpty(reference future) {
621 return PushIfEmpty(kGetFutureCore(future));
622 }
623
624 pointer PopIfAvailable() {
625 return kGetFutureImpl(BaseFutureList::PopIfAvailable());
626 }
627
628 reference Pop() { return *kGetFutureImpl(BaseFutureList::PopIfAvailable()); }
629
630 template <typename Resolver>
631 void ResolveAllWith(Resolver&& resolver) {
632 while (!list().empty()) {
633 resolver(Pop());
634 }
635 }
636
637 template <typename Resolver>
638 void ResolveOneWith(Resolver&& resolver) {
639 if (!list().empty()) {
640 resolver(Pop());
641 }
642 }
643};
644
649template <auto kMemberPtr>
652
654
655} // namespace pw::async2
Definition: intrusive_forward_list.h:99
Definition: future.h:547
void ResolveOne()
Definition: future.h:569
void ResolveAll()
Pops all futures and calls WakeAndMarkReady() on them.
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
constexpr FutureCore(ReadyForCompletion)
Definition: future.h:464
constexpr FutureCore(Pending)
Definition: future.h:460
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 Wake()
Definition: future.h:486
bool is_pendable() const
Definition: future.h:472
void Unlist()
Removes this future from its list, if it is in one.
Definition: future.h:496
bool in_list() const
Definition: future.h:513
Definition: future.h:65
Poll< value_type > Pend(Context &cx)
Definition: future.h:76
bool is_complete() const
Definition: future.h:88
Definition: future.h:138
bool empty()
Returns true if there are no futures listed.
Definition: future.h:163
Lock & lock()
Provides access to the list's internal lock.
Definition: future.h:169
void Push(FutureType &future)
Adds a future to the end of the list.
Definition: future.h:146
std::optional< std::reference_wrapper< FutureType > > Pop()
Removes and returns the first future from the list, if one exists.
Definition: future.h:152
Definition: future.h:286
void Wake()
Wakes the task waiting on the future.
Definition: future.h:389
ListableFutureWithWaker(ConstructedState state)
Definition: future.h:351
ConstructedState
Tag to prevent accidental default construction.
Definition: future.h:337
Definition: poll.h:60
Definition: future.h:195
bool has_future()
Returns true if the provider has a future.
Definition: future.h:223
bool TrySet(FutureType &future)
Attempts to set the provider's future, returning true if successful.
Definition: future.h:209
std::optional< std::reference_wrapper< FutureType > > Take()
Claims the provider's future, leaving it unset.
Definition: future.h:218
void Set(FutureType &future)
Sets the provider's future. Crashes if a future is already set.
Definition: future.h:203
Definition: intrusive_list.h:88
constexpr bool IsReady() const noexcept
Returns whether or not this value is Ready.
Definition: poll.h:133
constexpr bool IsPending() const noexcept
Returns whether or not this value is Pending.
Definition: poll.h:136
#define PW_ASYNC_STORE_WAKER(context, waker_or_queue_out, wait_reason_string)
Definition: waker.h:60
void push_back(T &item)
Inserts an element at the end of the list.
Definition: intrusive_list.h:255
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
bool empty() const noexcept
Definition: intrusive_list.h:207
void pop_front()
Removes the first item in the list. The list must not be empty.
Definition: intrusive_list.h:264
T & front()
Reference to the first element in the list. Undefined behavior if empty().
Definition: intrusive_list.h:167
#define PW_LOCKABLE(name)
Definition: lock_annotations.h:208
#define PW_NO_LOCK_SAFETY_ANALYSIS
Definition: lock_annotations.h:292
#define PW_EXCLUSIVE_LOCK_FUNCTION(...)
Definition: lock_annotations.h:230
#define PW_UNLOCK_FUNCTION(...)
Definition: lock_annotations.h:247
Definition: poll.h:36
Definition: future.h:99