Condy v1.9
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
execution.hpp
Go to the documentation of this file.
1
5
6#pragma once
7
9
10#ifdef CONDY_DETAIL_HAS_EXECUTION
11
13#include "condy/runtime.hpp"
14#include <type_traits>
15
16namespace condy {
17namespace detail {
18
19class Scheduler {
20public:
21 using scheduler_concept = ex::scheduler_tag;
22
23 Scheduler(Runtime &runtime) : runtime_(&runtime) {}
24
25 bool operator==(const Scheduler &other) const noexcept {
26 return runtime_ == other.runtime_;
27 }
28
29 ex::forward_progress_guarantee
30 query(ex::get_forward_progress_guarantee_t) const noexcept {
31 return ex::forward_progress_guarantee::weakly_parallel;
32 }
33
38 auto schedule() const noexcept { return ScheduleSender{*runtime_}; }
39
44 Runtime &runtime() const noexcept { return *runtime_; }
45
46private:
47 template <typename Receiver>
48 class OperationState
49 : public InvokerAdapter<OperationState<Receiver>, WorkInvoker> {
50 public:
51 using operation_state_concept = ex::operation_state_tag;
52
53 OperationState(Runtime &runtime, Receiver receiver)
54 : runtime_(runtime), receiver_(std::move(receiver)) {}
55
56 CONDY_DELETE_COPY_MOVE(OperationState);
57
58 public:
59 void start() noexcept { runtime_.schedule_internal(this); }
60
61 void invoke() noexcept { ex::set_value(std::move(receiver_)); }
62
63 private:
64 Runtime &runtime_;
65 Receiver receiver_;
66 };
67
68 class ScheduleSender {
69 public:
70 using sender_concept = ex::sender_tag;
71
72 using completion_signatures =
73 ex::completion_signatures<ex::set_value_t()>;
74
75 template <typename...>
76 static consteval auto get_completion_signatures() noexcept
77 -> completion_signatures {
78 return {};
79 }
80
81 ScheduleSender(Runtime &runtime) : runtime_(runtime) {}
82
83 template <typename Receiver> auto connect(Receiver receiver) {
84 return OperationState<std::decay_t<Receiver>>(runtime_,
85 std::move(receiver));
86 }
87
88 struct Env {
89 Runtime &runtime;
90 Scheduler query(ex::get_completion_scheduler_t<ex::set_value_t>)
91 const noexcept {
92 return Scheduler{runtime};
93 }
94 };
95 Env get_env() const noexcept { return {runtime_}; }
96
97 private:
98 Runtime &runtime_;
99 };
100
101private:
102 Runtime *runtime_;
103};
104
105template <typename R> struct set_value_traits {
106 using type = ex::set_value_t(R);
107};
108template <typename R1, typename R2> struct set_value_traits<std::pair<R1, R2>> {
109 using type = ex::set_value_t(R1, R2);
110};
111template <typename... R> struct set_value_traits<std::tuple<R...>> {
112 using type = ex::set_value_t(R...);
113};
114template <typename R>
115using set_value_traits_t = typename set_value_traits<R>::type;
116
117template <typename SenderImpl>
118class [[nodiscard]] StandardSender : public SenderImpl {
119public:
120 using SenderImpl::SenderImpl;
121
122 using sender_concept = ex::sender_tag;
123 using completion_signatures = ex::completion_signatures<
124 set_value_traits_t<typename SenderImpl::ReturnType>,
125 ex::set_error_t(std::error_code), ex::set_stopped_t()>;
126
127 template <typename...>
128 static consteval auto get_completion_signatures() noexcept
129 -> completion_signatures {
130 return {};
131 }
132
133 template <typename Receiver> auto connect(Receiver receiver) noexcept {
134 using OpState = decltype(this->connect_impl(
135 ReceiverWrapper<Receiver>{std::move(receiver)}));
136 return OperationStateWrapper<OpState>{
137 this->connect_impl(ReceiverWrapper<Receiver>{std::move(receiver)})};
138 }
139
140 // All condy senders are affine
141 auto affine() && noexcept { return std::move(*this); }
142
143private:
144 template <typename Receiver> struct ReceiverWrapper {
145 Receiver receiver;
146
147 void operator()(int32_t res) noexcept {
148 if (res >= 0) {
149 ex::set_value(std::move(receiver), res);
150 } else if (res == -ECANCELED) {
151 ex::set_stopped(std::move(receiver));
152 } else {
153 ex::set_error(std::move(receiver),
154 std::error_code(-res, std::generic_category()));
155 }
156 }
157
158 template <typename T>
159 void operator()(std::pair<int32_t, T> res) noexcept {
160 auto &[res_code, payload] = res;
161 if (res_code >= 0) {
162 ex::set_value(std::move(receiver), std::move(res_code),
163 std::move(payload));
164 } else if (res_code == -ECANCELED) {
165 ex::set_stopped(std::move(receiver));
166 } else {
167 ex::set_error(
168 std::move(receiver),
169 std::error_code(-res_code, std::generic_category()));
170 }
171 }
172
173 template <typename... T>
174 void operator()(std::tuple<T...> res) noexcept {
175 std::apply(
176 [this](auto &&...args) {
177 ex::set_value(std::move(receiver),
178 std::forward<decltype(args)>(args)...);
179 },
180 std::move(res));
181 }
182
183 template <typename T> void operator()(T &&res) noexcept {
184 ex::set_value(std::move(receiver), std::forward<T>(res));
185 }
186
187 auto get_stop_token() const noexcept {
188 auto env = ex::get_env(receiver);
189 return ex::get_stop_token(env);
190 }
191 };
192
193 template <typename OperationState> struct OperationStateWrapper {
194 using operation_state_concept = ex::operation_state_tag;
195
196 OperationState op_state;
197 void start() noexcept { op_state.start(0); }
198 };
199};
200
201template <typename... Ts> struct wait_variant {
202 static_assert(sizeof...(Ts) == 1, "Requires only one value type");
203};
204template <typename T> struct wait_variant<T> {
205 using type = T;
206};
207template <typename... Ts>
208using wait_variant_t = typename wait_variant<Ts...>::type;
209
210template <typename Sender> class [[nodiscard]] WaitSenderAwaiter {
211private:
212 struct Env {
213 Runtime &runtime;
214 Scheduler query(ex::get_scheduler_t) const noexcept {
215 return Scheduler{runtime};
216 }
217 Scheduler query(ex::get_start_scheduler_t) const noexcept {
218 return Scheduler{runtime};
219 }
220 Scheduler query(ex::get_delegation_scheduler_t) const noexcept {
221 return Scheduler{runtime};
222 }
223 };
224 using ValueType =
225 ex::value_types_of_t<Sender, Env, std::tuple, wait_variant_t>;
226
227public:
228 WaitSenderAwaiter(Sender sender, Runtime &runtime)
229 : runtime_(runtime),
230 op_state_(ex::connect(std::move(sender), Receiver{this})) {}
231
232 CONDY_DELETE_COPY_MOVE(WaitSenderAwaiter);
233
234public:
235 bool await_ready() const noexcept { return false; }
236
237 template <typename Promise>
238 bool await_suspend(std::coroutine_handle<Promise> handle) noexcept {
239 runtime_.pend_work_internal();
240 ex::start(op_state_);
241 auto h = std::exchange(handle_, handle);
242 return h == std::noop_coroutine();
243 }
244
245 std::optional<ValueType> await_resume() {
246 runtime_.resume_work_internal();
247 auto &result = *result_;
248 if (result.index() == 1) {
249 std::rethrow_exception(std::get<1>(std::move(result)));
250 }
251 if (result.index() == 2) {
252 return std::nullopt;
253 }
254 return std::get<0>(std::move(result));
255 }
256
257private:
258 struct Receiver {
259 using receiver_concept = ex::receiver_tag;
260 WaitSenderAwaiter *self;
261
262 template <typename... Args> void set_value(Args &&...args) noexcept {
263 self->complete_value_(std::forward<Args>(args)...);
264 }
265
266 template <typename E> void set_error(E &&e) noexcept {
267 self->complete_error_(std::forward<E>(e));
268 }
269
270 void set_stopped() noexcept { self->complete_stopped_(); }
271
272 auto get_env() const noexcept { return Env{self->runtime_}; }
273 };
274
275 template <typename E> static std::exception_ptr to_exception_(E &&e) {
276 if constexpr (std::is_same_v<std::decay_t<E>, std::exception_ptr>) {
277 return std::forward<E>(e);
278 } else if constexpr (std::is_same_v<std::decay_t<E>, std::error_code>) {
279 return std::make_exception_ptr(
280 std::system_error(std::forward<E>(e)));
281 } else {
282 return std::make_exception_ptr(std::forward<E>(e));
283 }
284 }
285
286 template <typename... Args> void complete_value_(Args &&...args) noexcept {
287 result_.emplace(std::in_place_index<0>,
288 ValueType(std::forward<Args>(args)...));
289 resume_();
290 }
291
292 template <typename E> void complete_error_(E &&e) noexcept {
293 result_.emplace(std::in_place_index<1>,
294 to_exception_(std::forward<E>(e)));
295 resume_();
296 }
297
298 void complete_stopped_() noexcept {
299 result_.emplace(std::in_place_index<2>, std::monostate{});
300 resume_();
301 }
302
303 void resume_() noexcept {
304 auto h = std::exchange(handle_, nullptr);
305 h.resume();
306 }
307
308 using ResultType =
309 std::variant<ValueType, std::exception_ptr, std::monostate>;
310
311 Runtime &runtime_;
312 ex::connect_result_t<Sender, Receiver> op_state_;
313 std::coroutine_handle<> handle_ = std::noop_coroutine();
314 std::optional<ResultType> result_;
315};
316
317} // namespace detail
318} // namespace condy
319
320#endif
Detection and aliasing of the available std::execution backend.
The main namespace for the Condy library.
Definition condy.hpp:38
Runtime type for running the io_uring event loop.
Internal utility classes and functions used by Condy.