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_sync/interrupt_spin_lock.h"
25
26namespace pw::async2::experimental {
27
59template <typename Derived, typename T>
60class Future {
61 public:
62 using value_type = T;
63
72 PW_ASSERT(!is_complete());
73 Poll<value_type> poll = derived().DoPend(cx);
74 if (poll.IsReady()) {
75 derived().DoMarkComplete();
76 }
77 return poll;
78 }
79
83 bool is_complete() const { return derived().DoIsComplete(); }
84
85 protected:
86 constexpr Future() = default;
87
88 private:
89 Derived& derived() { return static_cast<Derived&>(*this); }
90 const Derived& derived() const { return static_cast<const Derived&>(*this); }
91};
92
93namespace internal {
94
95template <typename D, typename V>
96std::true_type IsFuture(const volatile Future<D, V>*);
97
98std::false_type IsFuture(...);
99
100} // namespace internal
101
102template <typename T>
103struct is_future : decltype(internal::IsFuture(std::declval<T*>())){};
104
105template <typename T>
106constexpr bool is_future_v =
108
132template <typename FutureType, typename Lock = sync::InterruptSpinLock>
134 public:
135 constexpr ListFutureProvider() = default;
136
137 ListFutureProvider(const ListFutureProvider&) = delete;
138 ListFutureProvider& operator=(const ListFutureProvider&) = delete;
139
141 void Push(FutureType& future) {
142 std::lock_guard lock(lock_);
143 futures_.push_back(future);
144 }
145
149 FutureType& Pop() {
150 std::lock_guard lock(lock_);
151 PW_ASSERT(!futures_.empty());
152 FutureType& future = futures_.front();
153 futures_.pop_front();
154 return future;
155 }
156
158 bool empty() {
159 std::lock_guard lock(lock_);
160 return futures_.empty();
161 }
162
164 Lock& lock() { return lock_; }
165
166 private:
167 friend FutureType;
168
169 template <typename, typename>
170 friend class ListableFutureWithWaker;
171
172 using LockType = Lock;
173
175 Lock lock_;
176};
177
189template <typename FutureType>
191 public:
192 constexpr SingleFutureProvider() = default;
193
195 SingleFutureProvider& operator=(const SingleFutureProvider&) = delete;
196
198 void Set(FutureType& future) {
199 PW_ASSERT(!has_future());
200 inner_.Push(future);
201 }
202
204 bool TrySet(FutureType& future) {
205 if (has_future()) {
206 return false;
207 }
208 inner_.Push(future);
209 return true;
210 }
211
214 [[nodiscard]] FutureType& Take() { return inner_.Pop(); }
215
217 bool has_future() { return !inner_.empty(); }
218
219 private:
220 template <typename, typename>
221 friend class ListableFutureWithWaker;
222 friend FutureType;
223
225};
226
277template <typename Derived, typename T>
279 : public Future<ListableFutureWithWaker<Derived, T>, T>,
280 public IntrusiveList<Derived>::Item {
281 public:
283 ListableFutureWithWaker& operator=(const ListableFutureWithWaker&) = delete;
284
285 protected:
287
289 enum MovedFromState { kMovedFrom };
290
295 : provider_(nullptr), complete_(true) {}
296
297 explicit ListableFutureWithWaker(Provider& provider) : provider_(&provider) {
298 provider.Push(derived());
299 }
300 explicit ListableFutureWithWaker(SingleFutureProvider<Derived>& single)
301 : ListableFutureWithWaker(single.inner_) {}
302
303 ~ListableFutureWithWaker() {
304 if (provider_ == nullptr) {
305 return;
306 }
307 std::lock_guard guard(lock());
308 if (!this->unlisted()) {
309 this->unlist();
310 }
311 }
312
313 void MoveFrom(ListableFutureWithWaker& other) {
314 complete_ = std::exchange(other.complete_, true);
315 provider_ = std::exchange(other.provider_, nullptr);
316 waker_ = std::move(other.waker_);
317
318 if (provider_) {
319 std::lock_guard guard(lock());
320 if (!other.unlisted()) {
321 this->replace(other);
322 }
323 }
324 }
325
326 Provider& provider() {
327 PW_ASSERT(provider_ != nullptr);
328 return *provider_;
329 }
330
331 typename Provider::LockType& lock() { return provider().lock(); }
332
334 void Wake() { std::move(waker_).Wake(); }
335
336 private:
338
339 friend Base;
340 friend Provider;
341
342 Poll<T> DoPend(Context& cx) {
343 static_assert(
344 std::is_same_v<std::remove_extent_t<decltype(Derived::kWaitReason)>,
345 const char>,
346 "kWaitReason must be a character array");
347
348 Poll<T> poll = derived().DoPend(cx);
349 if (poll.IsPending()) {
350 PW_ASYNC_STORE_WAKER(cx, waker_, Derived::kWaitReason);
351 }
352 return poll;
353 }
354
355 void DoMarkComplete() { complete_ = true; }
356 bool DoIsComplete() const { return complete_; }
357
358 Derived& derived() { return static_cast<Derived&>(*this); }
359
360 Provider* provider_;
361 Waker waker_;
362 bool complete_ = false;
363};
364
365} // namespace pw::async2::experimental
Definition: context.h:55
Definition: poll.h:60
Definition: future.h:60
Poll< value_type > Pend(Context &cx)
Definition: future.h:71
bool is_complete() const
Definition: future.h:83
FutureType & Pop()
Definition: future.h:149
void Push(FutureType &future)
Adds a future to the end of the list.
Definition: future.h:141
Lock & lock()
Provides access to the list's internal lock.
Definition: future.h:164
bool empty()
Returns true if there are no futures listed.
Definition: future.h:158
void Wake()
Wakes the task waiting on the future.
Definition: future.h:334
ListableFutureWithWaker(MovedFromState)
Definition: future.h:294
MovedFromState
Tag to prevent accidental default construction.
Definition: future.h:289
void Set(FutureType &future)
Sets the provider's future. Crashes if a future is already set.
Definition: future.h:198
bool TrySet(FutureType &future)
Attempts to set the provider's future, returning true if successful.
Definition: future.h:204
FutureType & Take()
Definition: future.h:214
bool has_future()
Returns true if the provider has a future.
Definition: future.h:217
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
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
Definition: future.h:103