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
61template <typename Derived, typename T>
62class Future {
63 public:
64 using value_type = std::conditional_t<std::is_void_v<T>, ReadyType, T>;
65
74 PW_ASSERT(!is_complete());
75 Poll<value_type> poll = derived().DoPend(cx);
76 if (poll.IsReady()) {
77 derived().DoMarkComplete();
78 }
79 return poll;
80 }
81
85 bool is_complete() const { return derived().DoIsComplete(); }
86
87 protected:
88 constexpr Future() = default;
89
90 private:
91 Derived& derived() { return static_cast<Derived&>(*this); }
92 const Derived& derived() const { return static_cast<const Derived&>(*this); }
93};
94
95namespace internal {
96
97template <typename D, typename V>
98std::true_type IsFuture(const volatile Future<D, V>*);
99
100std::false_type IsFuture(...);
101
102} // namespace internal
103
104template <typename T>
105struct is_future : decltype(internal::IsFuture(std::declval<T*>())){};
106
107template <typename T>
108constexpr bool is_future_v =
110
134template <typename FutureType, typename Lock = sync::InterruptSpinLock>
136 public:
137 constexpr ListFutureProvider() = default;
138
139 ListFutureProvider(const ListFutureProvider&) = delete;
140 ListFutureProvider& operator=(const ListFutureProvider&) = delete;
141
143 void Push(FutureType& future) {
144 std::lock_guard lock(lock_);
145 futures_.push_back(future);
146 }
147
151 FutureType& Pop() {
152 std::lock_guard lock(lock_);
153 PW_ASSERT(!futures_.empty());
154 FutureType& future = futures_.front();
155 futures_.pop_front();
156 return future;
157 }
158
160 bool empty() {
161 std::lock_guard lock(lock_);
162 return futures_.empty();
163 }
164
166 Lock& lock() { return lock_; }
167
168 private:
169 friend FutureType;
170
171 template <typename, typename>
172 friend class ListableFutureWithWaker;
173
174 using LockType = Lock;
175
177 Lock lock_;
178};
179
191template <typename FutureType>
193 public:
194 constexpr SingleFutureProvider() = default;
195
197 SingleFutureProvider& operator=(const SingleFutureProvider&) = delete;
198
200 void Set(FutureType& future) {
201 PW_ASSERT(!has_future());
202 inner_.Push(future);
203 }
204
206 bool TrySet(FutureType& future) {
207 if (has_future()) {
208 return false;
209 }
210 inner_.Push(future);
211 return true;
212 }
213
216 [[nodiscard]] FutureType& Take() { return inner_.Pop(); }
217
219 bool has_future() { return !inner_.empty(); }
220
221 private:
222 template <typename, typename>
223 friend class ListableFutureWithWaker;
224 friend FutureType;
225
227};
228
279template <typename Derived, typename T>
281 : public Future<ListableFutureWithWaker<Derived, T>, T>,
282 public IntrusiveList<Derived>::Item {
283 public:
285 ListableFutureWithWaker& operator=(const ListableFutureWithWaker&) = delete;
286
287 protected:
290 class PW_LOCKABLE("pw::async2::experimental::ListableFutureWithWaker::Lock")
291 Provider {
292 public:
293 void lock() PW_EXCLUSIVE_LOCK_FUNCTION() {
294 if (provider_ != nullptr) {
295 provider_->lock().lock();
296 }
297 }
298
299 void unlock() PW_UNLOCK_FUNCTION() {
300 if (provider_ != nullptr) {
301 provider_->lock().unlock();
302 }
303 }
304
305 Provider& operator=(ListFutureProvider<Derived>* provider) {
306 provider_ = provider;
307 return *this;
308 }
309
310 ListFutureProvider<Derived>* get() const { return provider_; }
311 ListFutureProvider<Derived>& operator*() const {
312 PW_ASSERT(provider_ != nullptr);
313 return *provider_;
314 }
315 ListFutureProvider<Derived>* operator->() const {
316 PW_ASSERT(provider_ != nullptr);
317 return provider_;
318 }
319
320 explicit operator bool() const { return provider_ != nullptr; }
321
322 private:
323 friend class ListableFutureWithWaker<Derived, T>;
324
325 explicit Provider(ListFutureProvider<Derived>* provider)
326 : provider_(provider) {}
327
329 };
330
331 using Lock = Provider;
332
334 enum ConstructedState { kMovedFrom, kReadyForCompletion };
335
349 : provider_(nullptr), complete_(state == kMovedFrom) {}
350
352 : provider_(&provider) {
353 provider.Push(derived());
354 }
355 explicit ListableFutureWithWaker(SingleFutureProvider<Derived>& single)
356 : ListableFutureWithWaker(single.inner_) {}
357
358 ~ListableFutureWithWaker() {
359 if (!provider_) {
360 return;
361 }
362 std::lock_guard guard(lock());
363 if (!this->unlisted()) {
364 this->unlist();
365 }
366 }
367
368 void MoveFrom(ListableFutureWithWaker& other) {
369 complete_ = std::exchange(other.complete_, true);
370 provider_ = std::exchange(other.provider_, nullptr);
371 waker_ = std::move(other.waker_);
372
373 if (provider_) {
374 std::lock_guard guard(lock());
375 if (!other.unlisted()) {
376 this->replace(other);
377 }
378 }
379 }
380
381 ListFutureProvider<Derived>& provider() { return *provider_; }
382
383 Lock& lock() { return provider_; }
384
386 void Wake() { std::move(waker_).Wake(); }
387
388 private:
390
391 friend Base;
393
395 static_assert(
396 std::is_same_v<std::remove_extent_t<decltype(Derived::kWaitReason)>,
397 const char>,
398 "kWaitReason must be a character array");
399
400 Poll<typename Base::value_type> poll = derived().DoPend(cx);
401 if (poll.IsPending()) {
402 PW_ASYNC_STORE_WAKER(cx, waker_, Derived::kWaitReason);
403 Relist();
404 }
405 return poll;
406 }
407
409 void Relist() {
410 if (provider_ && this->unlisted()) {
411 provider_->Push(derived());
412 }
413 }
414
415 void DoMarkComplete() { complete_ = true; }
416 bool DoIsComplete() const { return complete_; }
417
418 Derived& derived() { return static_cast<Derived&>(*this); }
419
420 Provider provider_;
421 Waker waker_;
422 bool complete_ = false;
423};
424
425} // namespace pw::async2::experimental
Definition: context.h:55
Definition: poll.h:60
Definition: future.h:62
Poll< value_type > Pend(Context &cx)
Definition: future.h:73
bool is_complete() const
Definition: future.h:85
FutureType & Pop()
Definition: future.h:151
void Push(FutureType &future)
Adds a future to the end of the list.
Definition: future.h:143
Lock & lock()
Provides access to the list's internal lock.
Definition: future.h:166
bool empty()
Returns true if there are no futures listed.
Definition: future.h:160
void Wake()
Wakes the task waiting on the future.
Definition: future.h:386
ConstructedState
Tag to prevent accidental default construction.
Definition: future.h:334
ListableFutureWithWaker(ConstructedState state)
Definition: future.h:348
void Set(FutureType &future)
Sets the provider's future. Crashes if a future is already set.
Definition: future.h:200
bool TrySet(FutureType &future)
Attempts to set the provider's future, returning true if successful.
Definition: future.h:206
FutureType & Take()
Definition: future.h:216
bool has_future()
Returns true if the provider has a future.
Definition: future.h:219
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
#define PW_LOCKABLE(name)
Definition: lock_annotations.h:208
#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:105