#pragma once #include #include #include namespace clice { /// An async coroutine that runs a callback on a worker thread. template class async { private: Callback callback; std::optional result; uv_work_t req; std::coroutine_handle<> handle; public: template requires std::is_invocable_v async(Fn&& fn) : callback(std::forward(fn)) {} bool await_ready(this const async& self) noexcept { return false; } void await_suspend(this async& self, std::coroutine_handle<> handle) noexcept { self.handle = handle; self.req.data = &self; uv_queue_work( uv_default_loop(), &self.req, [](uv_work_t* req) { auto& self = *static_cast(req->data); self.result = self.callback(); }, [](uv_work_t* req, int status) { if(status != 0) { // TODO: handle error } auto& self = *static_cast(req->data); self->handle.resume(); }); } decltype(auto) await_resume(this async& self) noexcept { assert(self.result.has_value()); return *self.result; } }; template async(T) -> async>; template class Task { public: struct promise_type { std::optional value; std::coroutine_handle<> continuation; Task get_return_object(this promise_type& self) { return Task{std::coroutine_handle::from_promise(self)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { if(continuation) { continuation.resume(); } return {}; } void return_value(T&& val) { value = std::move(val); } void return_value(const T& val) { value = val; } void unhandled_exception() { std::terminate(); } }; Task(std::coroutine_handle handle) : handle(handle) {} ~Task() { if(handle && !handle.done()) { handle.destroy(); } } T get() { return std::move(*handle.promise().value); } bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> awaiting) noexcept { handle.promise().continuation = awaiting; handle.resume(); } T await_resume() { return std::move(*handle.promise().value); } private: std::coroutine_handle handle; }; template <> class Task { public: struct promise_type { std::coroutine_handle<> continuation; Task get_return_object(this promise_type& self) { return Task{std::coroutine_handle::from_promise(self)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { if(continuation) { continuation.resume(); } return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; Task(std::coroutine_handle handle) : handle(handle) {} ~Task() { if(handle && !handle.done()) { handle.destroy(); } } bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> awaiting) noexcept { handle.promise().continuation = awaiting; handle.resume(); } void await_resume() {} private: std::coroutine_handle handle; }; } // namespace clice