From 15f265725410333edf79eb4831c8e12e291c5b20 Mon Sep 17 00:00:00 2001 From: Paul Mathieu Date: Sun, 19 Jun 2022 09:32:09 +0200 Subject: [PATCH] arm: async changes - more tracing - pending resume() notifications - idle function returns bool (useful for testing) may not build (more stuff coming in later commits) --- arm/async.cc | 96 ++++++++++++++++++++++++++++++--------- arm/async.h | 125 ++++++++++++++++++++++++++++++++++++--------------- arm/main.cc | 1 + arm/trace.cc | 53 +++++++++++++++++----- arm/trace.h | 40 +++++++++++++++-- 5 files changed, 245 insertions(+), 70 deletions(-) diff --git a/arm/async.cc b/arm/async.cc index 3b1be35..5e2ec74 100644 --- a/arm/async.cc +++ b/arm/async.cc @@ -3,6 +3,23 @@ #include #include #include +#include + +#include "trace.h" + +#ifdef __x86_64__ +#include +struct InterruptLock { + static std::mutex m; + InterruptLock() { m.lock(); } + ~InterruptLock() { m.unlock(); } +}; +std::mutex InterruptLock::m; +#else // __x86_64__ +#include "lock.h" + +using tracing::trace; +#endif // __x86_64__ namespace async { namespace { @@ -16,13 +33,19 @@ struct Stuff { Stuff* next; }; +struct Notification { + bool pending; // can only be true if stuff is nullptr + Stuff* stuff; +}; + std::atomic work; -std::array, static_cast(AwaitableType::kNumTypes)> - queues; +std::array(AwaitableType::kNumTypes)> + notifications = {}; } // namespace void schedule(std::coroutine_handle<> h, int ms) { + TRACE(tracing::TraceEvent::kAsyncSchedule); std::chrono::system_clock::time_point exp = std::chrono::system_clock::now() + std::chrono::milliseconds(ms); Stuff* news = new Stuff{ @@ -47,10 +70,12 @@ void schedule(std::coroutine_handle<> h, int ms) { s->next = news; } -void main_loop(void (*idle_function)()) { +void main_loop(bool (*idle_function)()) { while (1) { if (idle_function != nullptr) { - idle_function(); + if (idle_function()) { + break; + }; } Stuff* stuff = work; if (stuff == nullptr) { @@ -64,38 +89,65 @@ void main_loop(void (*idle_function)()) { continue; } + TRACE(tracing::TraceEvent::kAsyncTask); stuff->h(); + TRACE(tracing::TraceEvent::kAsyncTaskDone); - Stuff* oldstuff = stuff; - work = stuff->next; + if (stuff->h.done()) { + stuff->h.destroy(); + } + { + InterruptLock lock; - delete oldstuff; + work = stuff->next; + } + delete stuff; } } void enqueue(std::coroutine_handle<> h, AwaitableType type) { - Stuff* item = new Stuff{.h = h}; auto ttype = static_cast(type); - Stuff* stuff = queues[ttype]; - if (stuff == nullptr) { - queues[ttype] = item; - return; + + { + InterruptLock lock; + TRACE(tracing::TraceEvent::kAsyncEnqueue); + + const bool was_notified = + std::exchange(notifications[ttype].pending, false); + if (was_notified) { + TRACE(tracing::TraceEvent::kAsyncAwaitWasNotified); + schedule(h); + return; + } + + Stuff* item = new Stuff{.h = h}; + Stuff* stuff = notifications[ttype].stuff; + if (stuff == nullptr) { + notifications[ttype].stuff = item; + return; + } + while (stuff->next != nullptr) { + stuff = stuff->next; + } + stuff->next = item; } - while (stuff->next != nullptr) { - stuff = stuff->next; - } - stuff->next = item; } void resume(AwaitableType type) { auto ttype = static_cast(type); - Stuff* stuff = queues[ttype]; - if (stuff == nullptr) { - return; - } + Stuff* stuff = nullptr; + { + InterruptLock lock; - queues[ttype] = stuff->next; - schedule(stuff->h); + stuff = notifications[ttype].stuff; + if (stuff == nullptr) { + notifications[ttype].pending = true; + return; + } + + notifications[ttype].stuff = stuff->next; + schedule(stuff->h); + } delete stuff; } diff --git a/arm/async.h b/arm/async.h index 219d7df..7acc00d 100644 --- a/arm/async.h +++ b/arm/async.h @@ -1,18 +1,29 @@ #pragma once #include -#include #include +#include "trace.h" + +#ifdef __clang__ +#include +namespace std { +using namespace experimental; +} +#else // __clang__ +#include +#endif // __clang__ + 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); } - h.destroy(); } void await_resume() noexcept(true) {} @@ -20,35 +31,7 @@ struct task_final_suspend { }; 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() {} - - T ret_value; - std::coroutine_handle<> parent; - }; - - // awaitable - bool await_ready() { return h.done(); } - void await_suspend(std::coroutine_handle<> ha) { - h(); - h.promise().parent = ha; - } - T await_resume() { return std::move(h.promise().ret_value); } - - std::coroutine_handle h; -}; +struct task; template <> struct task { @@ -64,22 +47,94 @@ struct task { return {.parent = parent}; } void return_void() {} - void unhandled_exception() {} + void unhandled_exception() { + TRACE(tracing::TraceEvent::kAsyncException); + } std::coroutine_handle<> parent; }; // awaitable - bool await_ready() { return h.done(); } - void await_suspend(std::coroutine_handle<> ha) { - h.promise().parent = ha; + 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, diff --git a/arm/main.cc b/arm/main.cc index bd3332a..89c31b7 100644 --- a/arm/main.cc +++ b/arm/main.cc @@ -60,6 +60,7 @@ int main() { if ((cnt++ % 100000) == 0) { ToggleLed(0); } + return false; }); // should never get here } diff --git a/arm/trace.cc b/arm/trace.cc index 8daad23..f2d2a08 100644 --- a/arm/trace.cc +++ b/arm/trace.cc @@ -1,6 +1,7 @@ #include "trace.h" #include +#include #include "itoa.h" #include "lock.h" @@ -9,30 +10,62 @@ namespace tracing { namespace { +struct Event { + uint32_t timestamp; + TraceEvent event; +}; + constexpr size_t kTraceBufferSize = 256; -std::array buffer; +std::array buffer; size_t write_ptr = 0; +size_t size = 0; } // namespace void trace(int raw_event) { trace(static_cast(raw_event)); } void trace(TraceEvent event) { - InterruptLock lock; + const std::chrono::system_clock::time_point now = + std::chrono::system_clock::now(); + const uint32_t uptime_ticks = now.time_since_epoch().count(); - buffer[write_ptr] = event; + { + InterruptLock lock; - write_ptr = (write_ptr + 1) % buffer.size(); + buffer[write_ptr] = {.timestamp = uptime_ticks, .event = event}; + + write_ptr = (write_ptr + 1) % buffer.size(); + size = std::min(size + 1, kTraceBufferSize); + +#if TRACE_DUMP_WHEN_FULL + if (size == kTraceBufferSize) { + dump(); + } +#endif // TRACE_DUMP_WHEN_FULL + } } void dump() { - std::rotate(buffer.begin(), buffer.begin() + write_ptr, buffer.end()); + InterruptLock lock; - char number[] = "00000000\r\n"; - - for (TraceEvent event : buffer) { - itoa(static_cast(event), number); - UartWriteCrash(number); + if (size == kTraceBufferSize) { + std::rotate(buffer.begin(), buffer.begin() + write_ptr, buffer.end()); } + + char number[] = "00000000"; + + UartWriteCrash("----\r\n"); + for (Event event : std::span{buffer}.subspan(0, size)) { + itoa(static_cast(event.timestamp), number); + UartWriteCrash(number); + UartWriteCrash(" "); + itoa(static_cast(event.event), number); + UartWriteCrash(number); + UartWriteCrash("\r\n"); + } + UartWriteCrash("----\r\n"); + + size = 0; + write_ptr = 0; } } // namespace tracing diff --git a/arm/trace.h b/arm/trace.h index f548fc0..a3b4b96 100644 --- a/arm/trace.h +++ b/arm/trace.h @@ -1,14 +1,48 @@ #pragma once +#define TRACE_DUMP_WHEN_FULL 0 + +#ifdef __x86_64__ +#include +#define TRACE(x) printf(#x "\n") +#else // __x86_64__ +#define TRACE(...) tracing::trace(__VA_ARGS__) +#endif // __x86_64__ + #include namespace tracing { enum class TraceEvent : uint8_t { kUnknown = 0, - kUartIsr, - kUartRxCb, - kUartTxCb, + kUartIsr = 1, + kUartRxCb = 2, + kUartTxCb = 3, + + kUartSend = 10, + kUartRecv = 11, + kUartTxBufferFull = 12, + kUartTxBufferNotFull = 13, + + kUartWriteDone = 20, + + kAsyncResume = 4, + kAsyncEnqueue = 5, + kAsyncTask = 6, + kAsyncResumeSetPending = 7, + kAsyncAwaitWasNotified = 8, + kAsyncSchedule = 9, + + kAsyncTaskDone = 14, + kAsyncException = 15, + kAsyncCallParent = 16, + kAsyncCallParentDone = 17, + kAsyncCoAwait = 18, + kAsyncSuspend = 19, + kAsyncDestroy = 21, + + kAsyncGimmeWaiting = 22, + kAsyncGimmeResume = 23, }; void trace(TraceEvent event);