#pragma once #include #include #include #include #include namespace basiccoro { namespace detail { template struct PromiseBase { auto get_return_object() { return std::coroutine_handle::from_promise(static_cast(*this)); } void unhandled_exception() { std::terminate(); } }; template requires std::movable || std::same_as struct ValuePromise : public PromiseBase { using value_type = T; T val; void return_value(T t) { val = std::move(t); } }; template struct ValuePromise : public PromiseBase { using value_type = void; void return_void() {} }; template class AwaitablePromise : public ValuePromise, T> { public: auto initial_suspend() { return std::suspend_never(); } auto final_suspend() noexcept { if (waiting_) { waiting_.resume(); if (waiting_.done()) { waiting_.destroy(); } waiting_ = nullptr; } return std::suspend_always(); } void storeWaiting(std::coroutine_handle<> handle) { if (waiting_) { throw std::runtime_error("AwaitablePromise::storeWaiting(): already waiting"); } waiting_ = handle; } ~AwaitablePromise() { if (waiting_) { waiting_.destroy(); } } private: std::coroutine_handle<> waiting_ = nullptr; }; template class TaskBase { public: using promise_type = Promise; TaskBase(); TaskBase(std::coroutine_handle handle); TaskBase(const TaskBase&) = delete; TaskBase(TaskBase&&); TaskBase& operator=(const TaskBase&) = delete; TaskBase& operator=(TaskBase&&); ~TaskBase(); bool done() const { return handle_.done(); } protected: std::coroutine_handle handle_; bool handleShouldBeDestroyed_; }; template TaskBase::TaskBase() : handle_(nullptr), handleShouldBeDestroyed_(false) {} template TaskBase::TaskBase(std::coroutine_handle handle) : handle_(handle) { // TODO: this whole system needs revamping with something like UniqueCoroutineHandle // and custom static interface to awaiter types - so await_suspend method would take in UniqueCoroutineHandle if (handle.done()) { // it is resonable to expect that if the coroutine is done before // the task creation, then the original stack is continued without suspending, // and coroutine needs to be destroyed with TaskBase object handleShouldBeDestroyed_ = true; } else { // otherwise the coroutine should be managed by object that it is awaiting handleShouldBeDestroyed_ = false; } } template TaskBase::TaskBase(TaskBase&& other) : handle_(other.handle_), handleShouldBeDestroyed_(std::exchange(other.handleShouldBeDestroyed_, false)) { } template TaskBase& TaskBase::operator=(TaskBase&& other) { handle_ = other.handle_; handleShouldBeDestroyed_ = std::exchange(other.handleShouldBeDestroyed_, false); return *this; } template TaskBase::~TaskBase() { if (handleShouldBeDestroyed_) { handle_.destroy(); } } } // namespace detail template class AwaitableTask : public detail::TaskBase> { using Base = detail::TaskBase>; public: using Base::Base; class awaiter; friend class awaiter; awaiter operator co_await() const; }; template struct AwaitableTask::awaiter { bool await_ready() { return task_.done(); } template void await_suspend(std::coroutine_handle handle) { task_.handle_.promise().storeWaiting(handle); } T await_resume() { if constexpr (!std::is_same_v) { return std::move(task_.handle_.promise().val); } } const AwaitableTask& task_; }; template typename AwaitableTask::awaiter AwaitableTask::operator co_await() const { return awaiter{*this}; } } // namespace basiccoro