#pragma once #include "task_result.h" #include #include #include namespace NActors { template class TTask; /** * This exception is commonly thrown when task is cancelled */ class TTaskCancelled : public std::exception { public: const char* what() const noexcept { return "Task cancelled"; } }; namespace NDetail { template class TTaskPromise; template using TTaskHandle = std::coroutine_handle>; template class TTaskAwaiter { public: explicit TTaskAwaiter(TTaskHandle handle) : Handle(handle) { Y_VERIFY_DEBUG(Handle); } TTaskAwaiter(TTaskAwaiter&& rhs) : Handle(std::exchange(rhs.Handle, {})) {} TTaskAwaiter& operator=(const TTaskAwaiter&) = delete; TTaskAwaiter& operator=(TTaskAwaiter&&) = delete; ~TTaskAwaiter() noexcept { if (Handle) { Handle.destroy(); } } // We can only await a task that has not started yet static bool await_ready() noexcept { return false; } // Some arbitrary continuation c suspended and awaits the task TTaskHandle await_suspend(std::coroutine_handle<> c) noexcept { Y_VERIFY_DEBUG(Handle); Handle.promise().SetContinuation(c); return Handle; } TTaskResult&& await_resume() noexcept { Y_VERIFY_DEBUG(Handle); return std::move(Handle.promise().Result); } private: TTaskHandle Handle; }; template class TTaskResultAwaiter final : public TTaskAwaiter { public: using TTaskAwaiter::TTaskAwaiter; T&& await_resume() { return TTaskAwaiter::await_resume().Value(); } }; template<> class TTaskResultAwaiter final : public TTaskAwaiter { public: using TTaskAwaiter::TTaskAwaiter; void await_resume() { TTaskAwaiter::await_resume().Value(); } }; template class TTaskResultHandlerBase { public: void unhandled_exception() noexcept { Result.SetException(std::current_exception()); } protected: TTaskResult Result; }; template class TTaskResultHandler : public TTaskResultHandlerBase { public: template void return_value(TResult&& value) { this->Result.SetValue(std::forward(value)); } }; template<> class TTaskResultHandler : public TTaskResultHandlerBase { public: void return_void() noexcept { this->Result.SetValue(); } }; template class TTaskPromise final : public TTaskResultHandler { friend class TTaskAwaiter; public: TTask get_return_object() noexcept; static auto initial_suspend() noexcept { return std::suspend_always{}; } struct TFinalSuspend { static bool await_ready() noexcept { return false; } static void await_resume() noexcept { Y_FAIL("unexpected coroutine resume"); } static std::coroutine_handle<> await_suspend(std::coroutine_handle> h) noexcept { auto next = std::exchange(h.promise().Continuation, std::noop_coroutine()); Y_VERIFY_DEBUG(next, "Task finished without a continuation"); return next; } }; static auto final_suspend() noexcept { return TFinalSuspend{}; } private: void SetContinuation(std::coroutine_handle<> continuation) noexcept { Y_VERIFY_DEBUG(!Continuation, "Task can only be awaited once"); Continuation = continuation; } private: std::coroutine_handle<> Continuation; }; } // namespace NDetail /** * Represents a task that has not been started yet */ template class TTask final { public: using promise_type = NDetail::TTaskPromise; using value_type = T; public: TTask() noexcept = default; explicit TTask(NDetail::TTaskHandle handle) noexcept : Handle(handle) {} TTask(TTask&& rhs) noexcept : Handle(std::exchange(rhs.Handle, {})) {} ~TTask() { if (Handle) { Handle.destroy(); } } TTask& operator=(TTask&& rhs) noexcept { if (Y_LIKELY(this != &rhs)) { auto handle = std::exchange(Handle, {}); Handle = std::exchange(rhs.Handle, {}); if (handle) { handle.destroy(); } } return *this; } /** * Returns true for a valid task object */ explicit operator bool() const noexcept { return bool(Handle); } /** * Starts task and returns TTaskResult when it completes */ auto WhenDone() && noexcept { Y_VERIFY_DEBUG(Handle, "Cannot await an empty task"); return NDetail::TTaskAwaiter(std::exchange(Handle, {})); } /** * Starts task and returns its result when it completes */ auto operator co_await() && noexcept { Y_VERIFY_DEBUG(Handle, "Cannot await an empty task"); return NDetail::TTaskResultAwaiter(std::exchange(Handle, {})); } private: NDetail::TTaskHandle Handle; }; namespace NDetail { template inline TTask TTaskPromise::get_return_object() noexcept { return TTask(TTaskHandle::from_promise(*this)); } } // namespace NDetail } // namespace NActors