#pragma once #include #include #include #include "trace.h" namespace async { struct task_final_suspend { bool await_ready() noexcept(true) { return false; } void await_suspend(std::coroutine_handle<> h) noexcept(true) { if (parent) { TRACE(tracing::TraceEvent::kAsyncCallParent); parent(); TRACE(tracing::TraceEvent::kAsyncCallParentDone); if (parent && parent.done()) { TRACE(tracing::TraceEvent::kAsyncDestroy); parent.destroy(); } } } void await_resume() noexcept(true) {} std::coroutine_handle<> parent; }; template struct gimme { // child interface bool await_ready() { return false; } void await_suspend(std::coroutine_handle<> h) { ha = h; waiting = true; TRACE(tracing::TraceEvent::kAsyncGimmeWaiting); } T await_resume() { waiting = false; TRACE(tracing::TraceEvent::kAsyncGimmeResume); return std::move(stuff); } // parent interface void feed(T&& s) { if (!waiting) { __builtin_trap(); } if (!ha) { __builtin_trap(); } stuff = s; ha.resume(); } bool waiting = false; std::coroutine_handle<> ha; T stuff; }; template struct task; template <> struct task { struct promise_type; using handle_type = std::coroutine_handle; struct promise_type { task get_return_object() { return {.h = handle_type::from_promise(*this)}; } std::suspend_always initial_suspend() noexcept { return {}; } task_final_suspend final_suspend() noexcept { return {.parent = parent}; } void return_void() {} void unhandled_exception() { TRACE(tracing::TraceEvent::kAsyncException); } std::coroutine_handle<> parent; }; // awaitable bool await_ready() { TRACE(tracing::TraceEvent::kAsyncCoAwait); h(); if (h.done()) { TRACE(tracing::TraceEvent::kAsyncDestroy); h.destroy(); return true; } return false; } void await_suspend(std::coroutine_handle<> ha) { TRACE(tracing::TraceEvent::kAsyncSuspend); h.promise().parent = ha; } void await_resume() {} std::coroutine_handle h; }; template struct task { struct promise_type; using handle_type = std::coroutine_handle; struct promise_type { task get_return_object() { return {.h = handle_type::from_promise(*this)}; } std::suspend_always initial_suspend() noexcept { return {}; } task_final_suspend final_suspend() noexcept { return {.parent = parent}; } void return_value(T&& value) { ret_value = std::move(value); } void unhandled_exception() { TRACE(tracing::TraceEvent::kAsyncException); } template From> task_final_suspend yield_value(From&& value) { ret_value = std::forward(value); result_ready = true; return {.parent = parent}; } T ret_value; bool result_ready = false; std::coroutine_handle<> parent; }; // awaitable bool await_ready() { h.promise().parent = {}; TRACE(tracing::TraceEvent::kAsyncCoAwait); h(); if (h.promise().result_ready) { return true; } if (h.done()) { destroyed = true; ret_value = std::move(h.promise().ret_value); TRACE(tracing::TraceEvent::kAsyncDestroy); h.destroy(); return true; } return false; } void await_suspend(std::coroutine_handle<> ha) { TRACE(tracing::TraceEvent::kAsyncSuspend); h.promise().parent = ha; } T await_resume() { if (!destroyed) { h.promise().result_ready = false; return std::move(h.promise().ret_value); } return std::move(ret_value); } bool destroyed = false; T ret_value; std::coroutine_handle h; }; enum class AwaitableType { kUnknown = 0, kUartRx = 1, kUartTx = 2, kNumTypes }; void schedule(std::coroutine_handle<> h, int ms = 0); void enqueue(std::coroutine_handle<> h, AwaitableType type); void resume(AwaitableType type); // typically called from an ISR void main_loop(bool (*idle_function)()); void step(); inline auto await(AwaitableType type) { struct awaitable { AwaitableType type; bool await_ready() { return false; }; void await_suspend(std::coroutine_handle<> h) { enqueue(h, type); } void await_resume() {} }; return awaitable{type}; } inline auto delay(int ms) { struct awaitable { int ms; bool await_ready() { return false; }; void await_suspend(std::coroutine_handle<> h) { schedule(h, ms); } void await_resume() {} }; return awaitable{ms}; } } // namespace async