Condy v1.8
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
task.hpp
Go to the documentation of this file.
1
7
8#pragma once
9
10#include "condy/coro.hpp"
13#include "condy/runtime.hpp"
14#include <coroutine>
15#include <future>
16
17namespace condy {
18namespace detail {
19
20template <typename T = void, typename Allocator = void> class TaskBase {
21public:
22 using PromiseType = typename Coro<T, Allocator>::promise_type;
23
24 TaskBase() : TaskBase(nullptr) {}
25 TaskBase(std::coroutine_handle<PromiseType> h) : handle_(h) {}
26 TaskBase(TaskBase &&other) noexcept
27 : handle_(std::exchange(other.handle_, nullptr)) {}
28 TaskBase &operator=(TaskBase &&other) noexcept {
29 if (this != &other) {
30 if (handle_) {
31 panic_on("Task destroyed without being awaited");
32 }
33 handle_ = std::exchange(other.handle_, nullptr);
34 }
35 return *this;
36 }
37
38 CONDY_DELETE_COPY(TaskBase);
39
40 ~TaskBase() {
41 if (handle_) {
42 panic_on("Task destroyed without being awaited");
43 }
44 }
45
46public:
54 void detach() noexcept {
55 handle_.promise().request_detach();
56 handle_ = nullptr;
57 }
58
63 bool joinable() const noexcept { return handle_ != nullptr; }
64
75 auto operator co_await() noexcept;
76
77protected:
78 static void wait_inner_(std::coroutine_handle<PromiseType> handle);
79
80protected:
81 std::coroutine_handle<PromiseType> handle_;
82};
83
84template <typename T, typename Allocator>
85void TaskBase<T, Allocator>::wait_inner_(
86 std::coroutine_handle<PromiseType> handle) {
87 if (Context::current().runtime() != nullptr) [[unlikely]] {
88 throw std::logic_error("Sync wait inside runtime");
89 }
90 if (handle == nullptr) [[unlikely]] {
91 throw std::invalid_argument("Task not joinable");
92 }
93 std::promise<void> prom;
94 auto fut = prom.get_future();
95 struct TaskWaiter : public InvokerAdapter<TaskWaiter> {
96 TaskWaiter(std::promise<void> &p) : prom_(p) {}
97
98 void invoke() noexcept { prom_.set_value(); }
99
100 std::promise<void> &prom_;
101 };
102
103 TaskWaiter waiter(prom);
104 if (handle.promise().request_join(&waiter)) {
105 // Still not finished, wait
106 fut.get();
107 }
108}
109
110template <typename T, typename Allocator>
111struct TaskAwaiterBase : public InvokerAdapter<TaskAwaiterBase<T, Allocator>> {
112 TaskAwaiterBase(
113 std::coroutine_handle<typename Coro<T, Allocator>::promise_type>
114 task_handle,
115 Runtime *runtime)
116 : task_handle_(task_handle), runtime_(runtime) {}
117
118 bool await_ready() const {
119 if (task_handle_ == nullptr) {
120 throw std::invalid_argument("Task not joinable");
121 }
122 return false;
123 }
124
125 template <typename PromiseType>
126 bool
127 await_suspend(std::coroutine_handle<PromiseType> caller_handle) noexcept {
128 Context::current().runtime()->pend_work_internal();
129 assert(runtime_ != nullptr);
130 caller_promise_ = &caller_handle.promise();
131 return task_handle_.promise().request_join(this);
132 }
133
134 void invoke() noexcept {
135 assert(caller_promise_ != nullptr);
136 runtime_->schedule_internal(caller_promise_);
137 }
138
139 std::coroutine_handle<typename Coro<T, Allocator>::promise_type>
140 task_handle_;
141 Runtime *runtime_ = nullptr;
142 WorkInvoker *caller_promise_ = nullptr;
143};
144
145template <typename T, typename Allocator>
146struct TaskAwaiter : public TaskAwaiterBase<T, Allocator> {
147 using Base = TaskAwaiterBase<T, Allocator>;
148 using Base::Base;
149
150 T await_resume() {
151 Context::current().runtime()->resume_work_internal();
152 auto exception = std::move(Base::task_handle_.promise()).exception();
153 if (exception) [[unlikely]] {
154 Base::task_handle_.destroy();
155 std::rethrow_exception(exception);
156 }
157 T value = std::move(Base::task_handle_.promise()).value();
158 Base::task_handle_.destroy();
159 return value;
160 }
161};
162
163template <typename Allocator>
164struct TaskAwaiter<void, Allocator> : public TaskAwaiterBase<void, Allocator> {
165 using Base = TaskAwaiterBase<void, Allocator>;
166 using Base::Base;
167
168 void await_resume() {
169 Context::current().runtime()->resume_work_internal();
170 auto exception = std::move(Base::task_handle_.promise()).exception();
171 Base::task_handle_.destroy();
172 if (exception) [[unlikely]] {
173 std::rethrow_exception(exception);
174 }
175 }
176};
177
178template <typename T, typename Allocator>
179inline auto TaskBase<T, Allocator>::operator co_await() noexcept {
180 return TaskAwaiter<T, Allocator>(std::exchange(handle_, nullptr),
181 Context::current().runtime());
182}
183
184struct [[nodiscard]] SwitchAwaiter {
185 bool await_ready() const noexcept { return false; }
186
187 template <typename PromiseType>
188 void await_suspend(std::coroutine_handle<PromiseType> handle) noexcept {
189 runtime_->schedule_internal(&handle.promise());
190 }
191
192 void await_resume() const noexcept {}
193
194 Runtime *runtime_;
195};
196
197} // namespace detail
198} // namespace condy
Coroutine definitions.
Polymorphic invocation utilities.
The main namespace for the Condy library.
Definition condy.hpp:37
Runtime type for running the io_uring event loop.