23template <
typename Handle, PrepFuncLike Func>
class OpSenderOperationState {
25 template <
typename... HandleArgs>
26 OpSenderOperationState(Func prep_func, HandleArgs &&...handle_args)
27 : prep_func_(std::move(prep_func)),
28 finish_handle_(std::forward<HandleArgs>(handle_args)...) {}
30 CONDY_DELETE_COPY_MOVE(OpSenderOperationState);
33 void start(
unsigned int flags)
noexcept {
34 auto *runtime = Context::current().runtime();
35 if (runtime ==
nullptr) {
39 if (finish_handle_.get().stop_requested()) {
43 auto &ring = runtime->ring_internal();
44 io_uring_sqe *sqe = prep_func_(&ring);
49 runtime->pend_work_internal();
50 io_uring_sqe_set_flags(sqe, sqe->flags | flags);
51 auto work = encode_work(&finish_handle_.get(), WorkType::Common);
52 io_uring_sqe_set_data64(sqe, work);
55 finish_handle_.get().maybe_set_cancel(runtime);
59 void fail_(int32_t res)
noexcept {
61 io_uring_cqe cqe[2] = {};
63 finish_handle_.get().handle(cqe);
68 HandleBox<Handle> finish_handle_;
71template <
unsigned int Flags,
typename Sender,
typename Receiver>
74 FlaggedOpState(Sender sender, Receiver receiver)
75 : op_state_(sender.connect_impl(std::move(receiver))) {}
77 CONDY_DELETE_COPY_MOVE(FlaggedOpState);
79 void start(
unsigned int flags)
noexcept { op_state_.start(flags | Flags); }
82 using OperationState = operation_state_t<Sender, Receiver>;
83 OperationState op_state_;
86template <
typename TokenType>
class WhenAnyCanceller {
88 auto chain_token(TokenType token)
noexcept {
89 if (token.stop_possible()) {
90 stop_callback_.emplace(std::move(token), Cancellation{
this});
92 return stop_source_.get_token();
95 template <
typename R>
void operator()(
const R &)
noexcept {
96 stop_source_.request_stop();
99 void reset() noexcept { stop_callback_.reset(); }
102 struct Cancellation {
103 WhenAnyCanceller *self;
104 void operator()() noexcept { self->cancel_(); }
106 void cancel_() noexcept { stop_source_.request_stop(); }
108 using StopCallbackType = stop_callback_t<TokenType, Cancellation>;
110 std::stop_source stop_source_;
111 std::optional<StopCallbackType> stop_callback_;
114template <
typename TokenType>
class WhenAllCanceller {
116 auto chain_token(TokenType token)
noexcept {
return token; }
118 template <
typename R>
void operator()(
const R &)
noexcept {}
120 void reset() noexcept {}
123template <
typename Receiver,
typename Canceller,
typename... Senders>
124class ParallelOperationState {
126 ParallelOperationState(std::tuple<Senders...> senders, Receiver receiver)
127 : receiver_(std::move(receiver)) {
128 auto next_token = canceller_.chain_token(receiver_.get_stop_token());
129 connect_senders_(senders, next_token);
132 CONDY_DELETE_COPY_MOVE(ParallelOperationState);
134 ~ParallelOperationState() {
135 std::apply([](
auto &&...states) { (states.destroy(), ...); },
139 void start(
unsigned int flags)
noexcept {
140 if constexpr (
sizeof...(Senders) == 0) {
141 std::move(receiver_)(
142 std::make_pair(std::move(order_), std::move(results_)));
145 [&](
auto &&...states) { (states.get().start(flags), ...); },
152 std::remove_cvref_t<decltype(std::declval<Canceller &>().chain_token(
153 std::declval<stop_token_t<Receiver>>()))>;
155 template <
size_t I = 0>
156 void connect_senders_(std::tuple<Senders...> &senders,
157 const TokenType &token)
noexcept {
158 if constexpr (I <
sizeof...(Senders)) {
159 std::get<I>(op_states_).accept([&] {
160 return std::move(std::get<I>(senders))
161 .connect_impl(ChildReceiver<I>{
this, token});
163 connect_senders_<I + 1>(senders, token);
167 template <
size_t I,
typename R>
void receive_(R &&result)
noexcept {
169 auto no = completed_count_++;
171 std::get<I>(results_) = std::forward<R>(result);
172 if (no + 1 ==
sizeof...(Senders)) {
174 std::move(receiver_)(
175 std::make_pair(std::move(order_), std::move(results_)));
179 template <
size_t I>
struct ChildReceiver {
180 ParallelOperationState *self;
181 TokenType stop_token;
182 template <
typename R>
void operator()(R &&result)
noexcept {
183 self->receive_<I>(std::forward<R>(result));
185 auto get_stop_token() const noexcept {
return stop_token; }
188 template <
typename T>
struct operation_state_traits;
189 template <
size_t... Is>
190 struct operation_state_traits<std::index_sequence<Is...>> {
191 using type = std::tuple<
192 RawStorage<operation_state_t<Senders, ChildReceiver<Is>>>...>;
194 using OperationStates =
typename operation_state_traits<
195 std::make_index_sequence<
sizeof...(Senders)>>::type;
198 OperationStates op_states_;
199 std::array<size_t,
sizeof...(Senders)> order_;
200 std::tuple<
typename Senders::ReturnType...> results_;
201 size_t completed_count_ = 0;
203 Canceller canceller_;
206template <
typename Receiver,
typename... Senders>
207using ParallelAnyOperationState =
208 ParallelOperationState<Receiver, WhenAnyCanceller<stop_token_t<Receiver>>,
211template <
typename Receiver,
typename... Senders>
212using ParallelAllOperationState =
213 ParallelOperationState<Receiver, WhenAllCanceller<stop_token_t<Receiver>>,
216template <
typename Receiver>
struct ReceiverAllWrapper {
218 ReceiverAllWrapper(Receiver receiver) : receiver(std::move(receiver)) {}
219 template <
typename R>
void operator()(R &&result)
noexcept {
220 auto &[order, results] = result;
221 std::move(receiver)(std::move(results));
223 auto get_stop_token() const noexcept {
return receiver.get_stop_token(); }
226template <
typename Receiver>
struct ReceiverAnyWrapper {
228 ReceiverAnyWrapper(Receiver receiver) : receiver(std::move(receiver)) {}
229 template <
typename R>
void operator()(R &&result)
noexcept {
230 auto &[order, results] = result;
231 size_t index = order[0];
232 std::move(receiver)(tuple_at(results, index));
234 auto get_stop_token() const noexcept {
return receiver.get_stop_token(); }
237template <
typename Receiver,
typename... Senders>
238using WhenAnyOperationState =
239 ParallelAnyOperationState<ReceiverAnyWrapper<Receiver>, Senders...>;
241template <
typename Receiver,
typename... Senders>
242using WhenAllOperationState =
243 ParallelAllOperationState<ReceiverAllWrapper<Receiver>, Senders...>;
245template <
typename Receiver,
unsigned int Flags,
typename... Senders>
246class LinkOperationState :
public WhenAllOperationState<Receiver, Senders...> {
248 using Base = WhenAllOperationState<Receiver, Senders...>;
251 void start(
unsigned int flags)
noexcept {
252 auto &ring = Context::current().runtime()->ring_internal();
253 ring.reserve_space(
sizeof...(Senders));
254 start_linked_operations_(flags);
258 template <
size_t I = 0>
259 void start_linked_operations_(
unsigned int flags)
noexcept {
260 if constexpr (I <
sizeof...(Senders)) {
261 auto &state = std::get<I>(Base::op_states_);
262 if constexpr (I <
sizeof...(Senders) - 1) {
263 state.get().start(flags | Flags);
265 state.get().start(flags);
267 start_linked_operations_<I + 1>(flags);
272template <
typename Receiver,
typename Canceller,
typename Sender>
273class RangedParallelOperationState {
275 RangedParallelOperationState(std::vector<Sender> senders, Receiver receiver)
276 : op_states_(senders.size()), order_(senders.size()),
277 results_(senders.size()), receiver_(std::move(receiver)) {
278 auto next_token = canceller_.chain_token(receiver_.get_stop_token());
279 for (
size_t i = 0; i < senders.size(); ++i) {
280 op_states_[i].accept([&] {
281 return std::move(senders[i])
282 .connect_impl(ChildReceiver{
this, i, next_token});
287 CONDY_DELETE_COPY_MOVE(RangedParallelOperationState);
289 ~RangedParallelOperationState() {
290 for (
auto &op_state : op_states_) {
295 void start(
unsigned int flags)
noexcept {
296 if (op_states_.empty()) {
297 std::move(receiver_)(
298 std::make_pair(std::move(order_), std::move(results_)));
300 for (
auto &op_state : op_states_) {
301 op_state.get().start(flags);
308 std::remove_cvref_t<decltype(std::declval<Canceller &>().chain_token(
309 std::declval<stop_token_t<Receiver>>()))>;
311 template <
typename R>
void receive_(
size_t index, R &&result)
noexcept {
313 size_t no = completed_count_++;
315 results_[index] = std::forward<R>(result);
316 if (no + 1 == op_states_.size()) {
317 std::move(receiver_)(
318 std::make_pair(std::move(order_), std::move(results_)));
322 struct ChildReceiver {
323 RangedParallelOperationState *self;
325 TokenType stop_token;
326 template <
typename R>
void operator()(R &&result)
noexcept {
327 self->receive_(index, std::forward<R>(result));
329 auto get_stop_token() const noexcept {
return stop_token; }
332 using OperationStates =
333 std::vector<RawStorage<operation_state_t<Sender, ChildReceiver>>>;
336 OperationStates op_states_;
337 std::vector<size_t> order_;
338 std::vector<typename Sender::ReturnType> results_;
339 size_t completed_count_ = 0;
341 Canceller canceller_;
344template <
typename Receiver,
typename Sender>
345using RangedParallelAllOperationState = RangedParallelOperationState<
346 Receiver, WhenAllCanceller<stop_token_t<Receiver>>, Sender>;
348template <
typename Receiver,
typename Sender>
349using RangedParallelAnyOperationState = RangedParallelOperationState<
350 Receiver, WhenAnyCanceller<stop_token_t<Receiver>>, Sender>;
352template <
typename Receiver>
353using ReceiverRangedAllWrapper = ReceiverAllWrapper<Receiver>;
355template <
typename Receiver>
struct ReceiverRangedAnyWrapper {
357 ReceiverRangedAnyWrapper(Receiver receiver)
358 : receiver(std::move(receiver)) {}
359 template <
typename R>
void operator()(R &&result)
noexcept {
360 auto &[order, results] = result;
361 size_t index = order[0];
362 std::move(receiver)(std::make_pair(index, std::move(results[index])));
364 auto get_stop_token() const noexcept {
return receiver.get_stop_token(); }
367template <
typename Receiver,
typename Sender>
368using RangedWhenAllOperationState =
369 RangedParallelAllOperationState<ReceiverRangedAllWrapper<Receiver>, Sender>;
371template <
typename Receiver,
typename Sender>
372using RangedWhenAnyOperationState =
373 RangedParallelAnyOperationState<ReceiverRangedAnyWrapper<Receiver>, Sender>;
375template <
typename Receiver,
unsigned int Flags,
typename Sender>
376class RangedLinkOperationState
377 :
public RangedWhenAllOperationState<Receiver, Sender> {
379 using Base = RangedWhenAllOperationState<Receiver, Sender>;
382 void start(
unsigned int flags)
noexcept {
383 auto &ring = Context::current().runtime()->ring_internal();
384 ring.reserve_space(Base::op_states_.size());
385 for (
size_t i = 0; i < Base::op_states_.size(); ++i) {
386 auto &op_state = Base::op_states_[i];
387 if (i < Base::op_states_.size() - 1) {
388 op_state.get().start(flags | Flags);
390 op_state.get().start(flags);
Definitions of finish handle types for asynchronous operations.
The main namespace for the Condy library.
Internal utility classes and functions used by Condy.