2023-06-03 06:44:28 +00:00
|
|
|
#pragma once
|
2022-05-17 03:56:25 +00:00
|
|
|
|
2023-06-03 06:33:01 +00:00
|
|
|
#include <coroutine>
|
2022-05-17 03:56:25 +00:00
|
|
|
#include <chrono>
|
|
|
|
#include <utility>
|
|
|
|
|
2022-06-19 07:32:09 +00:00
|
|
|
#include "trace.h"
|
|
|
|
|
2022-05-17 03:56:25 +00:00
|
|
|
namespace async {
|
2022-05-17 17:17:56 +00:00
|
|
|
|
|
|
|
struct task_final_suspend {
|
|
|
|
bool await_ready() noexcept(true) { return false; }
|
|
|
|
void await_suspend(std::coroutine_handle<> h) noexcept(true) {
|
|
|
|
if (parent) {
|
2022-06-19 07:32:09 +00:00
|
|
|
TRACE(tracing::TraceEvent::kAsyncCallParent);
|
2022-05-17 17:17:56 +00:00
|
|
|
parent();
|
2022-06-19 07:32:09 +00:00
|
|
|
TRACE(tracing::TraceEvent::kAsyncCallParentDone);
|
2023-06-03 06:33:01 +00:00
|
|
|
|
|
|
|
if (parent && parent.done()) {
|
|
|
|
TRACE(tracing::TraceEvent::kAsyncDestroy);
|
|
|
|
parent.destroy();
|
|
|
|
}
|
2022-05-17 17:17:56 +00:00
|
|
|
}
|
2022-05-17 03:56:25 +00:00
|
|
|
}
|
2022-05-17 17:17:56 +00:00
|
|
|
void await_resume() noexcept(true) {}
|
|
|
|
|
2022-05-17 03:56:25 +00:00
|
|
|
std::coroutine_handle<> parent;
|
|
|
|
};
|
|
|
|
|
2023-06-03 06:33:01 +00:00
|
|
|
template <typename T>
|
|
|
|
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;
|
|
|
|
};
|
2022-06-19 07:45:04 +00:00
|
|
|
|
2022-05-17 17:17:56 +00:00
|
|
|
template <typename T = void>
|
2022-06-19 07:32:09 +00:00
|
|
|
struct task;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct task<void> {
|
2022-05-17 17:17:56 +00:00
|
|
|
struct promise_type;
|
|
|
|
using handle_type = std::coroutine_handle<promise_type>;
|
|
|
|
|
|
|
|
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};
|
|
|
|
}
|
2022-06-19 07:32:09 +00:00
|
|
|
void return_void() {}
|
|
|
|
void unhandled_exception() {
|
|
|
|
TRACE(tracing::TraceEvent::kAsyncException);
|
|
|
|
}
|
2022-05-17 17:17:56 +00:00
|
|
|
|
|
|
|
std::coroutine_handle<> parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
// awaitable
|
2022-06-19 07:32:09 +00:00
|
|
|
bool await_ready() {
|
|
|
|
TRACE(tracing::TraceEvent::kAsyncCoAwait);
|
2022-05-17 17:17:56 +00:00
|
|
|
h();
|
2022-06-19 07:32:09 +00:00
|
|
|
if (h.done()) {
|
2023-06-03 06:33:01 +00:00
|
|
|
TRACE(tracing::TraceEvent::kAsyncDestroy);
|
|
|
|
h.destroy();
|
2022-06-19 07:32:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void await_suspend(std::coroutine_handle<> ha) {
|
|
|
|
TRACE(tracing::TraceEvent::kAsyncSuspend);
|
2022-05-17 17:17:56 +00:00
|
|
|
h.promise().parent = ha;
|
|
|
|
}
|
2022-06-19 07:32:09 +00:00
|
|
|
void await_resume() {}
|
2022-05-17 17:17:56 +00:00
|
|
|
|
|
|
|
std::coroutine_handle<promise_type> h;
|
|
|
|
};
|
|
|
|
|
2022-06-19 07:32:09 +00:00
|
|
|
template <typename T>
|
|
|
|
struct task {
|
2022-05-17 17:17:56 +00:00
|
|
|
struct promise_type;
|
|
|
|
using handle_type = std::coroutine_handle<promise_type>;
|
|
|
|
|
|
|
|
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};
|
|
|
|
}
|
2022-06-19 07:32:09 +00:00
|
|
|
void return_value(T&& value) { ret_value = std::move(value); }
|
|
|
|
void unhandled_exception() {
|
|
|
|
TRACE(tracing::TraceEvent::kAsyncException);
|
|
|
|
}
|
|
|
|
template<std::convertible_to<T> From>
|
|
|
|
task_final_suspend yield_value(From&& value) {
|
|
|
|
ret_value = std::forward<From>(value);
|
|
|
|
result_ready = true;
|
|
|
|
return {.parent = parent};
|
|
|
|
}
|
2022-05-17 17:17:56 +00:00
|
|
|
|
2022-06-19 07:32:09 +00:00
|
|
|
T ret_value;
|
|
|
|
bool result_ready = false;
|
2022-05-17 17:17:56 +00:00
|
|
|
std::coroutine_handle<> parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
// awaitable
|
2022-06-19 07:32:09 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-05-17 17:17:56 +00:00
|
|
|
void await_suspend(std::coroutine_handle<> ha) {
|
2022-06-19 07:32:09 +00:00
|
|
|
TRACE(tracing::TraceEvent::kAsyncSuspend);
|
2022-05-17 17:17:56 +00:00
|
|
|
h.promise().parent = ha;
|
2022-05-17 03:56:25 +00:00
|
|
|
}
|
2022-06-19 07:32:09 +00:00
|
|
|
T await_resume() {
|
|
|
|
if (!destroyed) {
|
|
|
|
h.promise().result_ready = false;
|
|
|
|
return std::move(h.promise().ret_value);
|
|
|
|
}
|
|
|
|
return std::move(ret_value);
|
|
|
|
}
|
2022-05-17 17:17:56 +00:00
|
|
|
|
2022-06-19 07:32:09 +00:00
|
|
|
bool destroyed = false;
|
|
|
|
T ret_value;
|
2022-05-17 17:17:56 +00:00
|
|
|
std::coroutine_handle<promise_type> h;
|
2022-05-17 03:56:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum class AwaitableType {
|
|
|
|
kUnknown = 0,
|
|
|
|
kUartRx = 1,
|
2022-05-17 17:17:56 +00:00
|
|
|
kUartTx = 2,
|
2022-05-17 03:56:25 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2022-06-19 07:46:04 +00:00
|
|
|
void main_loop(bool (*idle_function)());
|
2023-06-03 06:33:01 +00:00
|
|
|
void step();
|
2022-05-17 03:56:25 +00:00
|
|
|
|
|
|
|
inline auto await(AwaitableType type) {
|
2022-05-17 17:17:56 +00:00
|
|
|
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};
|
2022-05-17 03:56:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline auto delay(int ms) {
|
2022-05-17 17:17:56 +00:00
|
|
|
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};
|
2022-05-17 03:56:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace async
|