22#include "pw_allocator/allocator.h"
23#include "pw_allocator/layout.h"
24#include "pw_assert/assert.h"
25#include "pw_async2/context.h"
26#include "pw_async2/future.h"
27#include "pw_containers/internal/optional.h"
32[[noreturn]]
void CrashDueToCoroutineAllocationFailure();
57 constexpr Allocator& allocator()
const {
return *allocator_; }
77template <
typename PromiseType>
85 : promise_handle_(std::move(promise_handle)) {}
90 : promise_handle_(std::move(other.promise_handle_)) {
91 other.promise_handle_ =
nullptr;
96 OwningCoroutineHandle& operator=(OwningCoroutineHandle&& other) {
98 promise_handle_ = std::move(other.promise_handle_);
99 other.promise_handle_ =
nullptr;
104 ~OwningCoroutineHandle() { Release(); }
110 [[nodiscard]]
bool IsValid()
const {
111 return promise_handle_.address() !=
nullptr;
117 [[nodiscard]] PromiseType& promise()
const {
118 return promise_handle_.promise();
124 [[nodiscard]]
bool done()
const {
return promise_handle_.done(); }
130 void resume() { promise_handle_.resume(); }
136 void* address = promise_handle_.address();
137 if (address !=
nullptr) {
138 Deallocator& dealloc = promise_handle_.promise().deallocator();
139 promise_handle_.destroy();
140 promise_handle_ =
nullptr;
141 dealloc.Deallocate(address);
147 std::coroutine_handle<PromiseType> promise_handle_;
151template <
typename CoroOrFuture,
typename PromiseType>
163enum class CoroPollState : uint8_t {
180 std::suspend_always initial_suspend() {
return {}; }
189 std::suspend_always final_suspend()
noexcept {
return {}; }
195 void unhandled_exception() { PW_ASSERT(
false); }
200 template <
typename... Args>
201 static void*
operator new(std::size_t size,
203 const Args&...)
noexcept {
204 return SharedNew(coro_cx, size,
alignof(std::max_align_t));
210 template <
typename... Args>
211 static void*
operator new(std::size_t size,
212 std::align_val_t align,
214 const Args&...)
noexcept {
215 return SharedNew(coro_cx, size,
static_cast<size_t>(align));
221 template <
typename MethodReceiver,
typename... Args>
222 static void*
operator new(std::size_t size,
223 const MethodReceiver&,
225 const Args&...)
noexcept {
226 return SharedNew(coro_cx, size,
alignof(std::max_align_t));
232 template <
typename MethodReceiver,
typename... Args>
233 static void*
operator new(std::size_t size,
234 std::align_val_t align,
235 const MethodReceiver&,
237 const Args&...)
noexcept {
238 return SharedNew(coro_cx, size,
static_cast<size_t>(align));
249 static void operator delete(
void*) {}
251 CoroPollState AdvanceAwaitable(
Context& cx) {
252 if (pending_awaitable_ ==
nullptr) {
253 return CoroPollState::kReady;
255 return pending_awaitable_func_(pending_awaitable_, cx);
258 Deallocator& deallocator()
const {
return dealloc_; }
260 template <
typename AwaitableType>
261 void SuspendAwaitable(AwaitableType& awaitable) {
262 pending_awaitable_ = &awaitable;
263 pending_awaitable_func_ = [](
void* obj,
Context& lambda_cx) {
264 return static_cast<AwaitableType*
>(obj)->Advance(lambda_cx);
270 : dealloc_(cx.allocator()), pending_awaitable_(
nullptr) {}
275 std::size_t align)
noexcept;
286 void* pending_awaitable_;
287 CoroPollState (*pending_awaitable_func_)(
void*,
Context&);
290template <
typename T,
typename Derived>
293 using value_type = T;
300 static Coro<T> get_return_object_on_allocation_failure();
303 void MarkNestedCoroutineAllocationFailure() {
304 output_->reset(CoroPollState::kAborted);
308 Context& cx() {
return *context_; }
314 this->output_ = &output;
321 template <
typename CoroOrFuture>
322 requires(!std::is_reference_v<CoroOrFuture>)
324 CoroOrFuture&& coro_or_future) {
325 return std::forward<CoroOrFuture>(coro_or_future);
328 template <
typename CoroOrFuture>
330 CoroOrFuture& coro_or_future) {
331 return &coro_or_future;
335 using CoroPromiseBase::CoroPromiseBase;
352 template <
typename... Args>
357 template <
typename MethodReceiver,
typename... Args>
362 template <std::convertible_to<T> From>
363 void return_value(From&& value) {
364 this->output() = std::forward<From>(value);
376 template <
typename... Args>
380 template <
typename MethodReceiver,
typename... Args>
385 void return_void() { this->output().emplace(); }
392template <
typename CoroOrFuture,
typename PromiseType>
396 using await_type = std::remove_pointer_t<CoroOrFuture>;
398 using value_type =
typename await_type::value_type;
401 : state_(std::move(coro_or_future)), is_ready_(
false) {}
405 state_.result.~Value();
407 state_.coro_or_future.~CoroOrFuture();
412 bool await_ready() {
return false; }
421 bool await_suspend(
const std::coroutine_handle<PromiseType>& promise)
424 Context& cx = promise.promise().cx();
425 if (Advance(cx) == CoroPollState::kPending) {
426 promise.promise().SuspendAwaitable(*
this);
432 bool await_suspend(
const std::coroutine_handle<PromiseType>& promise)
435 Context& cx = promise.promise().cx();
436 CoroPollState state = Advance(cx);
437 if (state == CoroPollState::kPending) {
438 promise.promise().SuspendAwaitable(*
this);
441 if (state == CoroPollState::kAborted) {
442 promise.promise().MarkNestedCoroutineAllocationFailure();
452 value_type&& await_resume()
453 requires(!std::is_void_v<value_type>)
457 return std::move(state_.result);
461 requires(std::is_void_v<value_type>)
470 CoroPollState Advance(
Context& cx)
475 return CoroPollState::kPending;
477 state_.coro_or_future.~CoroOrFuture();
478 new (&state_.result) Value(std::move(*poll_res));
480 return CoroPollState::kReady;
483 CoroPollState Advance(
Context& cx)
487 return CoroPollState::kAborted;
489 auto result = get().Pend(cx);
490 if (result.state() == CoroPollState::kReady) {
491 state_.coro_or_future.~CoroOrFuture();
492 new (&state_.result) Value(std::move(*result));
495 return result.state();
500 std::conditional_t<std::is_void_v<value_type>,
ReadyType, value_type>;
503 if constexpr (std::is_pointer_v<CoroOrFuture>) {
504 return *state_.coro_or_future;
506 return state_.coro_or_future;
511 State(CoroOrFuture&& c_or_f) : coro_or_future(std::move(c_or_f)) {}
514 CoroOrFuture coro_or_future;
573 [[nodiscard]]
bool ok()
const {
return promise_handle_.IsValid(); }
581 template <
typename,
typename>
583 template <
typename, ReturnValuePolicy>
585 template <
typename,
typename E, ReturnValuePolicy>
586 requires std::invocable<E>
594 using enum internal::CoroPollState;
597 internal::CrashDueToCoroutineAllocationFailure();
601 internal::CoroPoll<T> return_value(kPending);
605 promise_handle_.promise().SetContextAndOutput(cx, return_value);
610 switch (promise_handle_.promise().AdvanceAwaitable(cx)) {
614 return_value.reset(kAborted);
615 promise_handle_.Release();
622 promise_handle_.resume();
626 if (return_value.state() != kPending) {
629 promise_handle_.Release();
639 explicit Coro(internal::OwningCoroutineHandle<promise_type>&& promise_handle)
640 : promise_handle_(std::move(promise_handle)) {}
642 internal::OwningCoroutineHandle<promise_type> promise_handle_;
645template <
typename Promise>
646Coro(internal::OwningCoroutineHandle<Promise>&&)
647 -> Coro<typename Promise::value_type>;
655template <
typename T,
typename Derived>
656Coro<T> TypedCoroPromise<T, Derived>::get_return_object() {
657 return Coro<T>(internal::OwningCoroutineHandle<Derived>(
658 std::coroutine_handle<CoroPromise<T>>::from_promise(
659 static_cast<Derived&
>(*
this))));
662template <
typename T,
typename Derived>
664TypedCoroPromise<T, Derived>::get_return_object_on_allocation_failure() {
665 return Coro<T>(internal::OwningCoroutineHandle<Derived>(
nullptr));
669template <
typename... Args>
673template <
typename First>
681template <
typename First,
typename Second,
typename... Others>
684 std::is_same<First, CoroContext>,
685 std::conjunction<std::is_same<Second, CoroContext>,
686 std::is_reference<First>,
687 std::is_class<std::remove_reference_t<First>>>> {};
695template <
typename T,
typename... Args>
696struct coroutine_traits<
pw::async2::Coro<T>, Args...> {
701 "CoroContext must be passed by value as the first argument to a "
702 "pw_async2 coroutine");
708 "pw_async2 coroutines must have exactly one CoroContext argument");
Definition: allocator.h:45
Abstract interface for releasing memory.
Definition: deallocator.h:29
Context required for creating and executing coroutines.
Definition: coro.h:45
constexpr CoroContext(Allocator &allocator)
Definition: coro.h:52
static Coro Empty()
Creates an empty, invalid coroutine object.
Definition: coro.h:565
T value_type
The type this coroutine returns from a co_return expression.
Definition: coro.h:562
bool ok() const
Definition: coro.h:573
Definition: coro_task.h:33
Definition: fallible_coro_task.h:34
OwningCoroutineHandle(std::coroutine_handle< PromiseType > &&promise_handle)
Take ownership of promise_handle.
Definition: coro.h:84
Definition: optional.h:65
constexpr bool IsPending() const noexcept
Returns whether or not this value is Pending.
Definition: poll.h:214
ReturnValuePolicy
Whether to store or discard the function's return value in RunOnceTask.
Definition: func_task.h:62
The Pigweed namespace.
Definition: alignment.h:27