10#ifdef CONDY_DETAIL_HAS_EXECUTION
21 using scheduler_concept = ex::scheduler_tag;
23 Scheduler(Runtime &runtime) : runtime_(&runtime) {}
25 bool operator==(
const Scheduler &other)
const noexcept {
26 return runtime_ == other.runtime_;
29 ex::forward_progress_guarantee
30 query(ex::get_forward_progress_guarantee_t)
const noexcept {
31 return ex::forward_progress_guarantee::weakly_parallel;
38 auto schedule() const noexcept {
return ScheduleSender{*runtime_}; }
44 Runtime &runtime() const noexcept {
return *runtime_; }
47 template <
typename Receiver>
49 :
public InvokerAdapter<OperationState<Receiver>, WorkInvoker> {
51 using operation_state_concept = ex::operation_state_tag;
53 OperationState(Runtime &runtime, Receiver receiver)
54 : runtime_(runtime), receiver_(std::move(receiver)) {}
56 CONDY_DELETE_COPY_MOVE(OperationState);
59 void start() noexcept { runtime_.schedule_internal(
this); }
61 void invoke() noexcept { ex::set_value(std::move(receiver_)); }
68 class ScheduleSender {
70 using sender_concept = ex::sender_tag;
72 using completion_signatures =
73 ex::completion_signatures<ex::set_value_t()>;
75 template <
typename...>
76 static consteval auto get_completion_signatures() noexcept
77 -> completion_signatures {
81 ScheduleSender(Runtime &runtime) : runtime_(runtime) {}
83 template <
typename Receiver>
auto connect(Receiver receiver) {
84 return OperationState<std::decay_t<Receiver>>(runtime_,
90 Scheduler query(ex::get_completion_scheduler_t<ex::set_value_t>)
92 return Scheduler{runtime};
95 Env get_env() const noexcept {
return {runtime_}; }
105template <
typename R>
struct set_value_traits {
106 using type = ex::set_value_t(R);
108template <
typename R1,
typename R2>
struct set_value_traits<std::pair<R1, R2>> {
109 using type = ex::set_value_t(R1, R2);
111template <
typename... R>
struct set_value_traits<std::tuple<R...>> {
112 using type = ex::set_value_t(R...);
115using set_value_traits_t =
typename set_value_traits<R>::type;
117template <
typename SenderImpl>
118class [[nodiscard]] StandardSender :
public SenderImpl {
120 using SenderImpl::SenderImpl;
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()>;
127 template <
typename...>
128 static consteval auto get_completion_signatures() noexcept
129 -> completion_signatures {
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)})};
141 auto affine() &&
noexcept {
return std::move(*
this); }
144 template <
typename Receiver>
struct ReceiverWrapper {
147 void operator()(int32_t res)
noexcept {
149 ex::set_value(std::move(receiver), res);
150 }
else if (res == -ECANCELED) {
151 ex::set_stopped(std::move(receiver));
153 ex::set_error(std::move(receiver),
154 std::error_code(-res, std::generic_category()));
158 template <
typename T>
159 void operator()(std::pair<int32_t, T> res)
noexcept {
160 auto &[res_code, payload] = res;
162 ex::set_value(std::move(receiver), std::move(res_code),
164 }
else if (res_code == -ECANCELED) {
165 ex::set_stopped(std::move(receiver));
169 std::error_code(-res_code, std::generic_category()));
173 template <
typename... T>
174 void operator()(std::tuple<T...> res)
noexcept {
176 [
this](
auto &&...args) {
177 ex::set_value(std::move(receiver),
178 std::forward<
decltype(args)>(args)...);
183 template <
typename T>
void operator()(T &&res)
noexcept {
184 ex::set_value(std::move(receiver), std::forward<T>(res));
187 auto get_stop_token() const noexcept {
188 auto env = ex::get_env(receiver);
189 return ex::get_stop_token(env);
193 template <
typename OperationState>
struct OperationStateWrapper {
194 using operation_state_concept = ex::operation_state_tag;
196 OperationState op_state;
197 void start() noexcept { op_state.start(0); }
201template <
typename... Ts>
struct wait_variant {
202 static_assert(
sizeof...(Ts) == 1,
"Requires only one value type");
204template <
typename T>
struct wait_variant<T> {
207template <
typename... Ts>
208using wait_variant_t =
typename wait_variant<Ts...>::type;
210template <
typename Sender>
class [[nodiscard]] WaitSenderAwaiter {
214 Scheduler query(ex::get_scheduler_t)
const noexcept {
215 return Scheduler{runtime};
217 Scheduler query(ex::get_start_scheduler_t)
const noexcept {
218 return Scheduler{runtime};
220 Scheduler query(ex::get_delegation_scheduler_t)
const noexcept {
221 return Scheduler{runtime};
225 ex::value_types_of_t<Sender, Env, std::tuple, wait_variant_t>;
228 WaitSenderAwaiter(Sender sender, Runtime &runtime)
230 op_state_(ex::connect(std::move(sender), Receiver{this})) {}
232 CONDY_DELETE_COPY_MOVE(WaitSenderAwaiter);
235 bool await_ready() const noexcept {
return false; }
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();
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)));
251 if (result.index() == 2) {
254 return std::get<0>(std::move(result));
259 using receiver_concept = ex::receiver_tag;
260 WaitSenderAwaiter *self;
262 template <
typename... Args>
void set_value(Args &&...args)
noexcept {
263 self->complete_value_(std::forward<Args>(args)...);
266 template <
typename E>
void set_error(E &&e)
noexcept {
267 self->complete_error_(std::forward<E>(e));
270 void set_stopped() noexcept { self->complete_stopped_(); }
272 auto get_env() const noexcept {
return Env{self->runtime_}; }
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)));
282 return std::make_exception_ptr(std::forward<E>(e));
286 template <
typename... Args>
void complete_value_(Args &&...args)
noexcept {
287 result_.emplace(std::in_place_index<0>,
288 ValueType(std::forward<Args>(args)...));
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)));
298 void complete_stopped_() noexcept {
299 result_.emplace(std::in_place_index<2>, std::monostate{});
303 void resume_() noexcept {
304 auto h = std::exchange(handle_,
nullptr);
309 std::variant<ValueType, std::exception_ptr, std::monostate>;
312 ex::connect_result_t<Sender, Receiver> op_state_;
313 std::coroutine_handle<> handle_ = std::noop_coroutine();
314 std::optional<ResultType> result_;
Detection and aliasing of the available std::execution backend.
The main namespace for the Condy library.
Runtime type for running the io_uring event loop.
Internal utility classes and functions used by Condy.