21template <
typename T =
void,
typename Allocator =
void>
class TaskBase {
23 using PromiseType =
typename Coro<T, Allocator>::promise_type;
25 TaskBase(std::coroutine_handle<PromiseType> h) : handle_(h) {}
26 TaskBase(TaskBase &&other) noexcept
27 : handle_(std::exchange(other.handle_,
nullptr)) {}
29 TaskBase(
const TaskBase &) =
delete;
30 TaskBase &operator=(
const TaskBase &) =
delete;
31 TaskBase &operator=(TaskBase &&other) =
delete;
35 panic_on(
"Task destroyed without being awaited");
47 void detach() noexcept {
48 handle_.promise().request_detach();
61 auto operator co_await()
noexcept;
64 static void wait_inner_(std::coroutine_handle<PromiseType> handle);
67 std::coroutine_handle<PromiseType> handle_;
70template <
typename T,
typename Allocator>
71void TaskBase<T, Allocator>::wait_inner_(
72 std::coroutine_handle<PromiseType> handle) {
73 if (Context::current().runtime() !=
nullptr) [[unlikely]] {
74 throw std::logic_error(
"Sync wait inside runtime");
76 std::promise<void> prom;
77 auto fut = prom.get_future();
78 struct TaskWaiter :
public InvokerAdapter<TaskWaiter> {
79 TaskWaiter(std::promise<void> &p) : prom_(p) {}
81 void invoke() { prom_.set_value(); }
83 std::promise<void> &prom_;
86 TaskWaiter waiter(prom);
87 if (handle.promise().register_task_await(&waiter)) {
106template <
typename T =
void,
typename Allocator =
void>
107class [[nodiscard]]
Task :
public TaskBase<T, Allocator> {
109 using Base = TaskBase<T, Allocator>;
122 auto handle = std::exchange(Base::handle_,
nullptr);
123 Base::wait_inner_(handle);
124 auto exception = std::move(handle.promise()).exception();
125 if (exception) [[unlikely]] {
127 std::rethrow_exception(exception);
129 T value = std::move(handle.promise()).value();
131 return std::move(value);
135template <
typename Allocator>
136class [[nodiscard]] Task<void, Allocator> :
public TaskBase<void, Allocator> {
138 using Base = TaskBase<void, Allocator>;
149 auto handle = std::exchange(Base::handle_,
nullptr);
150 Base::wait_inner_(handle);
151 auto exception = std::move(handle.promise()).exception();
153 if (exception) [[unlikely]] {
154 std::rethrow_exception(exception);
159template <
typename T,
typename Allocator>
160struct TaskAwaiterBase :
public InvokerAdapter<TaskAwaiterBase<T, Allocator>> {
162 std::coroutine_handle<
typename Coro<T, Allocator>::promise_type>
165 : task_handle_(task_handle), runtime_(runtime) {}
167 bool await_ready() const noexcept {
return false; }
169 template <
typename PromiseType>
171 await_suspend(std::coroutine_handle<PromiseType> caller_handle)
noexcept {
172 Context::current().runtime()->pend_work();
173 assert(runtime_ !=
nullptr);
174 caller_promise_ = &caller_handle.promise();
175 return task_handle_.promise().register_task_await(
this);
179 assert(caller_promise_ !=
nullptr);
180 runtime_->schedule(caller_promise_);
183 std::coroutine_handle<typename Coro<T, Allocator>::promise_type>
185 Runtime *runtime_ =
nullptr;
186 WorkInvoker *caller_promise_ =
nullptr;
189template <
typename T,
typename Allocator>
190struct TaskAwaiter :
public TaskAwaiterBase<T, Allocator> {
191 using Base = TaskAwaiterBase<T, Allocator>;
195 Context::current().runtime()->resume_work();
196 auto exception = std::move(Base::task_handle_.promise()).exception();
197 if (exception) [[unlikely]] {
198 Base::task_handle_.destroy();
199 std::rethrow_exception(exception);
201 T value = std::move(Base::task_handle_.promise()).value();
202 Base::task_handle_.destroy();
207template <
typename Allocator>
208struct TaskAwaiter<void, Allocator> :
public TaskAwaiterBase<void, Allocator> {
209 using Base = TaskAwaiterBase<void, Allocator>;
212 void await_resume() {
213 Context::current().runtime()->resume_work();
214 auto exception = std::move(Base::task_handle_.promise()).exception();
215 Base::task_handle_.destroy();
216 if (exception) [[unlikely]] {
217 std::rethrow_exception(exception);
222template <
typename T,
typename Allocator>
223inline auto TaskBase<T, Allocator>::operator
co_await()
noexcept {
224 return TaskAwaiter<T, Allocator>(std::exchange(handle_,
nullptr),
225 Context::current().runtime());
239template <
typename T,
typename Allocator>
241 auto handle = coro.release();
242 auto &promise = handle.promise();
243 promise.set_auto_destroy(
false);
245 runtime.schedule(&promise);
257template <
typename T,
typename Allocator>
259 auto *runtime = Context::current().runtime();
260 if (runtime ==
nullptr) [[unlikely]] {
261 throw std::logic_error(
"No runtime to spawn coroutine task");
263 return co_spawn(*runtime, std::move(coro));
268struct [[nodiscard]] SwitchAwaiter {
269 bool await_ready() const noexcept {
return false; }
271 template <
typename PromiseType>
272 void await_suspend(std::coroutine_handle<PromiseType> handle)
noexcept {
273 runtime_->schedule(&handle.promise());
276 void await_resume() const noexcept {}
Coroutine type used to define a coroutine function.
Definition coro.hpp:26
The event loop runtime for executing asynchronous.
Definition runtime.hpp:76
Coroutine task that runs concurrently in the runtime.
Definition task.hpp:107
T wait()
Wait synchronously for the task to complete and get the result.
Definition task.hpp:121
Polymorphic invocation utilities.
The main namespace for the Condy library.
Definition condy.hpp:28
detail::SwitchAwaiter co_switch(Runtime &runtime)
Switch current coroutine task to the given runtime.
Definition task.hpp:291
Task< T, Allocator > co_spawn(Runtime &runtime, Coro< T, Allocator > coro)
Spawn a coroutine as a task in the given runtime.
Definition task.hpp:240
Runtime type for running the io_uring event loop.