21template <
typename T =
void,
typename Allocator =
void>
class TaskBase {
23 using PromiseType =
typename Coro<T, Allocator>::promise_type;
25 TaskBase() : TaskBase(nullptr) {}
26 TaskBase(std::coroutine_handle<PromiseType> h) : handle_(h) {}
27 TaskBase(TaskBase &&other) noexcept
28 : handle_(std::exchange(other.handle_,
nullptr)) {}
29 TaskBase &operator=(TaskBase &&other)
noexcept {
32 panic_on(
"Task destroyed without being awaited");
34 handle_ = std::exchange(other.handle_,
nullptr);
39 TaskBase(
const TaskBase &) =
delete;
40 TaskBase &operator=(
const TaskBase &) =
delete;
44 panic_on(
"Task destroyed without being awaited");
56 void detach() noexcept {
57 handle_.promise().request_detach();
65 bool joinable() const noexcept {
return handle_ !=
nullptr; }
77 auto operator co_await()
noexcept;
80 static void wait_inner_(std::coroutine_handle<PromiseType> handle);
83 std::coroutine_handle<PromiseType> handle_;
86template <
typename T,
typename Allocator>
87void TaskBase<T, Allocator>::wait_inner_(
88 std::coroutine_handle<PromiseType> handle) {
89 if (detail::Context::current().runtime() !=
nullptr) [[unlikely]] {
90 throw std::logic_error(
"Sync wait inside runtime");
92 if (handle ==
nullptr) [[unlikely]] {
93 throw std::invalid_argument(
"Task not joinable");
95 std::promise<void> prom;
96 auto fut = prom.get_future();
97 struct TaskWaiter :
public InvokerAdapter<TaskWaiter> {
98 TaskWaiter(std::promise<void> &p) : prom_(p) {}
100 void invoke() { prom_.set_value(); }
102 std::promise<void> &prom_;
105 TaskWaiter waiter(prom);
106 if (handle.promise().register_task_await(&waiter)) {
125template <
typename T =
void,
typename Allocator =
void>
126class [[nodiscard]]
Task :
public TaskBase<T, Allocator> {
128 using Base = TaskBase<T, Allocator>;
142 auto handle = std::exchange(Base::handle_,
nullptr);
143 Base::wait_inner_(handle);
144 auto exception = std::move(handle.promise()).exception();
145 if (exception) [[unlikely]] {
147 std::rethrow_exception(exception);
149 T value = std::move(handle.promise()).value();
151 return std::move(value);
155template <
typename Allocator>
156class [[nodiscard]] Task<void, Allocator> :
public TaskBase<void, Allocator> {
158 using Base = TaskBase<void, Allocator>;
170 auto handle = std::exchange(Base::handle_,
nullptr);
171 Base::wait_inner_(handle);
172 auto exception = std::move(handle.promise()).exception();
174 if (exception) [[unlikely]] {
175 std::rethrow_exception(exception);
180template <
typename T,
typename Allocator>
181struct TaskAwaiterBase :
public InvokerAdapter<TaskAwaiterBase<T, Allocator>> {
183 std::coroutine_handle<
typename Coro<T, Allocator>::promise_type>
186 : task_handle_(task_handle), runtime_(runtime) {}
188 bool await_ready()
const {
189 if (task_handle_ ==
nullptr) {
190 throw std::invalid_argument(
"Task not joinable");
195 template <
typename PromiseType>
197 await_suspend(std::coroutine_handle<PromiseType> caller_handle)
noexcept {
198 detail::Context::current().runtime()->pend_work();
199 assert(runtime_ !=
nullptr);
200 caller_promise_ = &caller_handle.promise();
201 return task_handle_.promise().register_task_await(
this);
205 assert(caller_promise_ !=
nullptr);
206 runtime_->schedule(caller_promise_);
209 std::coroutine_handle<typename Coro<T, Allocator>::promise_type>
211 Runtime *runtime_ =
nullptr;
212 WorkInvoker *caller_promise_ =
nullptr;
215template <
typename T,
typename Allocator>
216struct TaskAwaiter :
public TaskAwaiterBase<T, Allocator> {
217 using Base = TaskAwaiterBase<T, Allocator>;
221 detail::Context::current().runtime()->resume_work();
222 auto exception = std::move(Base::task_handle_.promise()).exception();
223 if (exception) [[unlikely]] {
224 Base::task_handle_.destroy();
225 std::rethrow_exception(exception);
227 T value = std::move(Base::task_handle_.promise()).value();
228 Base::task_handle_.destroy();
233template <
typename Allocator>
234struct TaskAwaiter<void, Allocator> :
public TaskAwaiterBase<void, Allocator> {
235 using Base = TaskAwaiterBase<void, Allocator>;
238 void await_resume() {
239 detail::Context::current().runtime()->resume_work();
240 auto exception = std::move(Base::task_handle_.promise()).exception();
241 Base::task_handle_.destroy();
242 if (exception) [[unlikely]] {
243 std::rethrow_exception(exception);
248template <
typename T,
typename Allocator>
249inline auto TaskBase<T, Allocator>::operator
co_await()
noexcept {
250 return TaskAwaiter<T, Allocator>(std::exchange(handle_,
nullptr),
251 detail::Context::current().runtime());
265template <
typename T,
typename Allocator>
267 auto handle = coro.release();
268 auto &promise = handle.promise();
269 promise.set_auto_destroy(
false);
271 runtime.schedule(&promise);
283template <
typename T,
typename Allocator>
285 auto *runtime = detail::Context::current().runtime();
286 if (runtime ==
nullptr) [[unlikely]] {
287 throw std::logic_error(
"No runtime to spawn coroutine task");
289 return co_spawn(*runtime, std::move(coro));
294struct [[nodiscard]] SwitchAwaiter {
295 bool await_ready() const noexcept {
return false; }
297 template <
typename PromiseType>
298 void await_suspend(std::coroutine_handle<PromiseType> handle)
noexcept {
299 runtime_->schedule(&handle.promise());
302 void await_resume() const noexcept {}
Coroutine type used to define a coroutine function.
The event loop runtime for executing asynchronous.
Coroutine task that runs concurrently in the runtime.
T wait()
Wait synchronously for the task to complete and get the result.
Polymorphic invocation utilities.
The main namespace for the Condy library.
detail::SwitchAwaiter co_switch(Runtime &runtime)
Switch current coroutine task to the given runtime.
Task< T, Allocator > co_spawn(Runtime &runtime, Coro< T, Allocator > coro)
Spawn a coroutine as a task in the given runtime.
Runtime type for running the io_uring event loop.