Condy v1.9
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
op_states.hpp
Go to the documentation of this file.
1
5
6#pragma once
7
8#include "condy/concepts.hpp"
13#include <array>
14#include <cstddef>
15#include <optional>
16#include <stop_token>
17#include <tuple>
18#include <vector>
19
20namespace condy {
21namespace detail {
22
23template <typename Handle, PrepFuncLike Func> class OpSenderOperationState {
24public:
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)...) {}
29
30 CONDY_DELETE_COPY_MOVE(OpSenderOperationState);
31
32public:
33 void start(unsigned int flags) noexcept {
34 auto *runtime = Context::current().runtime();
35 if (runtime == nullptr) {
36 fail_(-EINVAL);
37 return;
38 }
39 if (finish_handle_.get().stop_requested()) {
40 fail_(-ECANCELED);
41 return;
42 }
43 auto &ring = runtime->ring_internal();
44 io_uring_sqe *sqe = prep_func_(&ring);
45 if (sqe == nullptr) {
46 fail_(-EINVAL);
47 return;
48 }
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);
53 ring.maybe_submit();
54
55 finish_handle_.get().maybe_set_cancel(runtime);
56 }
57
58private:
59 void fail_(int32_t res) noexcept {
60 assert(res < 0);
61 io_uring_cqe cqe[2] = {};
62 cqe[0].res = res;
63 finish_handle_.get().handle(cqe);
64 }
65
66private:
67 Func prep_func_;
68 HandleBox<Handle> finish_handle_;
69};
70
71template <unsigned int Flags, typename Sender, typename Receiver>
72class FlaggedOpState {
73public:
74 FlaggedOpState(Sender sender, Receiver receiver)
75 : op_state_(sender.connect_impl(std::move(receiver))) {}
76
77 CONDY_DELETE_COPY_MOVE(FlaggedOpState);
78
79 void start(unsigned int flags) noexcept { op_state_.start(flags | Flags); }
80
81private:
82 using OperationState = operation_state_t<Sender, Receiver>;
83 OperationState op_state_;
84};
85
86template <typename TokenType> class WhenAnyCanceller {
87public:
88 auto chain_token(TokenType token) noexcept {
89 if (token.stop_possible()) {
90 stop_callback_.emplace(std::move(token), Cancellation{this});
91 }
92 return stop_source_.get_token();
93 }
94
95 template <typename R> void operator()(const R &) noexcept {
96 stop_source_.request_stop();
97 }
98
99 void reset() noexcept { stop_callback_.reset(); }
100
101private:
102 struct Cancellation {
103 WhenAnyCanceller *self;
104 void operator()() noexcept { self->cancel_(); }
105 };
106 void cancel_() noexcept { stop_source_.request_stop(); }
107
108 using StopCallbackType = stop_callback_t<TokenType, Cancellation>;
109
110 std::stop_source stop_source_;
111 std::optional<StopCallbackType> stop_callback_;
112};
113
114template <typename TokenType> class WhenAllCanceller {
115public:
116 auto chain_token(TokenType token) noexcept { return token; }
117
118 template <typename R> void operator()(const R &) noexcept {}
119
120 void reset() noexcept {}
121};
122
123template <typename Receiver, typename Canceller, typename... Senders>
124class ParallelOperationState {
125public:
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);
130 }
131
132 CONDY_DELETE_COPY_MOVE(ParallelOperationState);
133
134 ~ParallelOperationState() {
135 std::apply([](auto &&...states) { (states.destroy(), ...); },
136 op_states_);
137 }
138
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_)));
143 } else {
144 std::apply(
145 [&](auto &&...states) { (states.get().start(flags), ...); },
146 op_states_);
147 }
148 }
149
150private:
151 using TokenType =
152 std::remove_cvref_t<decltype(std::declval<Canceller &>().chain_token(
153 std::declval<stop_token_t<Receiver>>()))>;
154
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});
162 });
163 connect_senders_<I + 1>(senders, token);
164 }
165 }
166
167 template <size_t I, typename R> void receive_(R &&result) noexcept {
168 canceller_(result);
169 auto no = completed_count_++;
170 order_[no] = I;
171 std::get<I>(results_) = std::forward<R>(result);
172 if (no + 1 == sizeof...(Senders)) {
173 canceller_.reset();
174 std::move(receiver_)(
175 std::make_pair(std::move(order_), std::move(results_)));
176 }
177 }
178
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));
184 }
185 auto get_stop_token() const noexcept { return stop_token; }
186 };
187
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>>>...>;
193 };
194 using OperationStates = typename operation_state_traits<
195 std::make_index_sequence<sizeof...(Senders)>>::type;
196
197protected:
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;
202 Receiver receiver_;
203 Canceller canceller_;
204};
205
206template <typename Receiver, typename... Senders>
207using ParallelAnyOperationState =
208 ParallelOperationState<Receiver, WhenAnyCanceller<stop_token_t<Receiver>>,
209 Senders...>;
210
211template <typename Receiver, typename... Senders>
212using ParallelAllOperationState =
213 ParallelOperationState<Receiver, WhenAllCanceller<stop_token_t<Receiver>>,
214 Senders...>;
215
216template <typename Receiver> struct ReceiverAllWrapper {
217 Receiver receiver;
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));
222 }
223 auto get_stop_token() const noexcept { return receiver.get_stop_token(); }
224};
225
226template <typename Receiver> struct ReceiverAnyWrapper {
227 Receiver receiver;
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));
233 }
234 auto get_stop_token() const noexcept { return receiver.get_stop_token(); }
235};
236
237template <typename Receiver, typename... Senders>
238using WhenAnyOperationState =
239 ParallelAnyOperationState<ReceiverAnyWrapper<Receiver>, Senders...>;
240
241template <typename Receiver, typename... Senders>
242using WhenAllOperationState =
243 ParallelAllOperationState<ReceiverAllWrapper<Receiver>, Senders...>;
244
245template <typename Receiver, unsigned int Flags, typename... Senders>
246class LinkOperationState : public WhenAllOperationState<Receiver, Senders...> {
247public:
248 using Base = WhenAllOperationState<Receiver, Senders...>;
249 using Base::Base;
250
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);
255 }
256
257private:
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);
264 } else {
265 state.get().start(flags);
266 }
267 start_linked_operations_<I + 1>(flags);
268 }
269 }
270};
271
272template <typename Receiver, typename Canceller, typename Sender>
273class RangedParallelOperationState {
274public:
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});
283 });
284 }
285 }
286
287 CONDY_DELETE_COPY_MOVE(RangedParallelOperationState);
288
289 ~RangedParallelOperationState() {
290 for (auto &op_state : op_states_) {
291 op_state.destroy();
292 }
293 }
294
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_)));
299 } else {
300 for (auto &op_state : op_states_) {
301 op_state.get().start(flags);
302 }
303 }
304 }
305
306private:
307 using TokenType =
308 std::remove_cvref_t<decltype(std::declval<Canceller &>().chain_token(
309 std::declval<stop_token_t<Receiver>>()))>;
310
311 template <typename R> void receive_(size_t index, R &&result) noexcept {
312 canceller_(result);
313 size_t no = completed_count_++;
314 order_[no] = index;
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_)));
319 }
320 }
321
322 struct ChildReceiver {
323 RangedParallelOperationState *self;
324 size_t index;
325 TokenType stop_token;
326 template <typename R> void operator()(R &&result) noexcept {
327 self->receive_(index, std::forward<R>(result));
328 }
329 auto get_stop_token() const noexcept { return stop_token; }
330 };
331
332 using OperationStates =
333 std::vector<RawStorage<operation_state_t<Sender, ChildReceiver>>>;
334
335protected:
336 OperationStates op_states_;
337 std::vector<size_t> order_;
338 std::vector<typename Sender::ReturnType> results_;
339 size_t completed_count_ = 0;
340 Receiver receiver_;
341 Canceller canceller_;
342};
343
344template <typename Receiver, typename Sender>
345using RangedParallelAllOperationState = RangedParallelOperationState<
346 Receiver, WhenAllCanceller<stop_token_t<Receiver>>, Sender>;
347
348template <typename Receiver, typename Sender>
349using RangedParallelAnyOperationState = RangedParallelOperationState<
350 Receiver, WhenAnyCanceller<stop_token_t<Receiver>>, Sender>;
351
352template <typename Receiver>
353using ReceiverRangedAllWrapper = ReceiverAllWrapper<Receiver>;
354
355template <typename Receiver> struct ReceiverRangedAnyWrapper {
356 Receiver receiver;
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])));
363 }
364 auto get_stop_token() const noexcept { return receiver.get_stop_token(); }
365};
366
367template <typename Receiver, typename Sender>
368using RangedWhenAllOperationState =
369 RangedParallelAllOperationState<ReceiverRangedAllWrapper<Receiver>, Sender>;
370
371template <typename Receiver, typename Sender>
372using RangedWhenAnyOperationState =
373 RangedParallelAnyOperationState<ReceiverRangedAnyWrapper<Receiver>, Sender>;
374
375template <typename Receiver, unsigned int Flags, typename Sender>
376class RangedLinkOperationState
377 : public RangedWhenAllOperationState<Receiver, Sender> {
378public:
379 using Base = RangedWhenAllOperationState<Receiver, Sender>;
380 using Base::Base;
381
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);
389 } else {
390 op_state.get().start(flags);
391 }
392 }
393 }
394};
395
396} // namespace detail
397
398} // namespace condy
Definitions of finish handle types for asynchronous operations.
The main namespace for the Condy library.
Definition condy.hpp:38
Internal utility classes and functions used by Condy.