synth/arm/async.h

208 lines
5.1 KiB
C
Raw Permalink Normal View History

2023-06-03 06:44:28 +00:00
#pragma once
2022-05-17 03:56:25 +00:00
#include <coroutine>
2022-05-17 03:56:25 +00:00
#include <chrono>
#include <utility>
#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) {
TRACE(tracing::TraceEvent::kAsyncCallParent);
2022-05-17 17:17:56 +00:00
parent();
TRACE(tracing::TraceEvent::kAsyncCallParentDone);
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;
};
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>
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};
}
void return_void() {}
void unhandled_exception() {
TRACE(tracing::TraceEvent::kAsyncException);
}
2022-05-17 17:17:56 +00:00
std::coroutine_handle<> parent;
};
// awaitable
bool await_ready() {
TRACE(tracing::TraceEvent::kAsyncCoAwait);
2022-05-17 17:17:56 +00:00
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);
2022-05-17 17:17:56 +00:00
h.promise().parent = ha;
}
void await_resume() {}
2022-05-17 17:17:56 +00:00
std::coroutine_handle<promise_type> h;
};
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};
}
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
T ret_value;
bool result_ready = false;
2022-05-17 17:17:56 +00:00
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;
}
2022-05-17 17:17:56 +00:00
void await_suspend(std::coroutine_handle<> ha) {
TRACE(tracing::TraceEvent::kAsyncSuspend);
2022-05-17 17:17:56 +00:00
h.promise().parent = ha;
2022-05-17 03:56:25 +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
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)());
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