#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 std::move(*self.result); } }; template async(T) -> async>; template class Task { public: struct promise_type; using handle_type = std::coroutine_handle; struct promise_type { std::optional value; std::coroutine_handle<> caller; Task get_return_object() { return Task(handle_type::from_promise(*this)); } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { if(caller) { caller.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(handle_type handle) : handle(handle) {} ~Task() { if(handle && !handle.done()) { handle.destroy(); } } void resume() { handle.resume(); } bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> caller) noexcept { handle.promise().caller = caller; handle.resume(); } T await_resume() { return std::move(*handle.promise().value); } private: handle_type handle; }; template <> class Task { public: struct promise_type; using handle_type = std::coroutine_handle; struct promise_type { std::coroutine_handle<> caller; Task get_return_object() { return Task(handle_type::from_promise(*this)); } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { if(caller) { caller.resume(); } return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; Task(handle_type handle) : handle(handle) {} //~Task() { // if(handle && !handle.done()) { // handle.destroy(); // } //} void resume() { handle.resume(); } bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> caller) noexcept { handle.promise().caller = caller; handle.resume(); } void await_resume() {} private: handle_type handle; }; } // namespace clice