25template <CQEHandlerLike CQEHandler>
26class OpFinishHandle :
public OpFinishHandleBase {
28 using ReturnType =
typename CQEHandler::ReturnType;
30 template <
typename... Args>
31 OpFinishHandle(Args &&...args) : cqe_handler_(std::forward<Args>(args)...) {
32 this->handle_func_ = handle_static_;
35 ReturnType extract_result() noexcept {
36 return cqe_handler_.extract_result();
39 void cancel(Runtime *runtime)
noexcept {
40 assert(runtime !=
nullptr);
41 runtime->cancel(
this);
45 static bool handle_static_(
void *data, io_uring_cqe *cqe)
noexcept {
46 auto *self =
static_cast<OpFinishHandle *
>(data);
47 return self->handle_impl_(cqe);
50 bool handle_impl_(io_uring_cqe *cqe)
noexcept {
51 cqe_handler_.handle_cqe(cqe);
52 assert(invoker_ !=
nullptr);
58 CQEHandler cqe_handler_;
61template <CQEHandlerLike CQEHandler,
typename Func>
62class MultiShotOpFinishHandle :
public OpFinishHandle<CQEHandler> {
64 template <
typename... Args>
65 MultiShotOpFinishHandle(Func func, Args &&...args)
66 : OpFinishHandle<CQEHandler>(std::forward<Args>(args)...),
67 func_(std::move(func)) {
68 this->handle_func_ = handle_static_;
72 static bool handle_static_(
void *data, io_uring_cqe *cqe)
noexcept {
73 auto *self =
static_cast<MultiShotOpFinishHandle *
>(data);
74 return self->handle_impl_(cqe);
77 bool handle_impl_(io_uring_cqe *cqe)
noexcept
79 if (cqe->flags & IORING_CQE_F_MORE) {
80 this->cqe_handler_.handle_cqe(cqe);
81 func_(this->cqe_handler_.extract_result());
84 this->cqe_handler_.handle_cqe(cqe);
85 assert(this->invoker_ !=
nullptr);
95template <CQEHandlerLike CQEHandler,
typename Func>
96class ZeroCopyOpFinishHandle :
public OpFinishHandle<CQEHandler> {
98 template <
typename... Args>
99 ZeroCopyOpFinishHandle(Func func, Args &&...args)
100 : OpFinishHandle<CQEHandler>(std::forward<Args>(args)...),
101 free_func_(std::move(func)) {
102 this->handle_func_ = handle_static_;
106 static bool handle_static_(
void *data, io_uring_cqe *cqe)
noexcept {
107 auto *self =
static_cast<ZeroCopyOpFinishHandle *
>(data);
108 return self->handle_impl_(cqe);
111 bool handle_impl_(io_uring_cqe *cqe)
noexcept
113 if (cqe->flags & IORING_CQE_F_MORE) {
114 this->cqe_handler_.handle_cqe(cqe);
115 assert(this->invoker_ !=
nullptr);
119 if (cqe->flags & IORING_CQE_F_NOTIF) {
126 this->cqe_handler_.handle_cqe(cqe);
127 assert(this->invoker_ !=
nullptr);
135 void notify_(int32_t res)
noexcept {
145constexpr bool is_nothrow_extract_result_v =
146 noexcept(std::declval<T>().extract_result());
148template <
bool Cancel, HandleLike Handle>
class RangedParallelFinishHandle {
150 using ChildReturnType =
typename Handle::ReturnType;
152 std::pair<std::vector<size_t>, std::vector<ChildReturnType>>;
154 void init(std::vector<Handle *> handles)
noexcept {
155 handles_ = std::move(handles);
156 child_invokers_.resize(handles_.size());
157 for (
size_t i = 0; i < handles_.size(); i++) {
158 auto *handle = handles_[i];
159 auto &invoker = child_invokers_[i];
160 invoker.self_ =
this;
162 handle->set_invoker(&invoker);
164 order_.resize(handles_.size());
167 void cancel(Runtime *runtime)
noexcept {
170 for (
auto &handle : handles_) {
171 handle->cancel(runtime);
176 ReturnType extract_result() noexcept(is_nothrow_extract_result_v<Handle>) {
177 std::vector<ChildReturnType> result;
178 result.reserve(handles_.size());
179 for (
size_t i = 0; i < handles_.size(); i++) {
180 result.push_back(handles_[i]->extract_result());
182 return std::make_pair(std::move(order_), std::move(result));
185 void set_invoker(Invoker *invoker)
noexcept { invoker_ = invoker; }
188 void finish_(
size_t idx)
noexcept {
189 size_t no = finished_count_++;
192 if constexpr (Cancel) {
195 auto *runtime = detail::Context::current().runtime();
196 for (
size_t i = 0; i < handles_.size(); i++) {
198 handles_[i]->cancel(runtime);
204 if (no == handles_.size() - 1) {
206 assert(invoker_ !=
nullptr);
213 struct FinishInvoker :
public InvokerAdapter<FinishInvoker> {
214 void invoke() noexcept { self_->finish_(no_); }
215 RangedParallelFinishHandle *self_;
219 size_t finished_count_ = 0;
220 bool canceled_ =
false;
221 std::vector<Handle *> handles_ = {};
222 std::vector<FinishInvoker> child_invokers_;
223 std::vector<size_t> order_;
224 Invoker *invoker_ =
nullptr;
227template <HandleLike Handle>
228using RangedParallelAllFinishHandle = RangedParallelFinishHandle<false, Handle>;
230template <HandleLike Handle>
231using RangedParallelAnyFinishHandle = RangedParallelFinishHandle<true, Handle>;
233template <HandleLike Handle>
234class RangedWhenAllFinishHandle :
public RangedParallelAllFinishHandle<Handle> {
236 using Base = RangedParallelAllFinishHandle<Handle>;
237 using ReturnType = std::vector<typename Handle::ReturnType>;
239 ReturnType extract_result() noexcept(noexcept(Base::extract_result())) {
240 auto r = Base::extract_result();
241 return std::move(r.second);
245template <HandleLike Handle>
246class RangedWhenAnyFinishHandle :
public RangedParallelAnyFinishHandle<Handle> {
248 using Base = RangedParallelAnyFinishHandle<Handle>;
249 using ChildReturnType =
typename Handle::ReturnType;
250 using ReturnType = std::pair<size_t, ChildReturnType>;
252 ReturnType extract_result() {
253 auto r = Base::extract_result();
254 auto &[order, results] = r;
257 auto idx = order.at(0);
258 return std::make_pair(idx, std::move(results[idx]));
262template <
bool Cancel, HandleLike... Handles>
class ParallelFinishHandle {
264 using ReturnType = std::pair<std::array<size_t,
sizeof...(Handles)>,
265 std::tuple<typename Handles::ReturnType...>>;
267 template <
typename... HandlePtr>
void init(HandlePtr... handles)
noexcept {
268 handles_ = std::make_tuple(handles...);
269 foreach_set_invoker_();
272 void cancel(Runtime *runtime)
noexcept {
276 [runtime](
auto *...handles) {
277 (handles->cancel(runtime), ...);
284 extract_result() noexcept((is_nothrow_extract_result_v<Handles> && ...)) {
285 auto result = std::apply(
286 [](
auto *...handle_ptrs) {
287 return std::make_tuple(handle_ptrs->extract_result()...);
290 return std::make_pair(std::move(order_), std::move(result));
293 void set_invoker(Invoker *invoker)
noexcept { invoker_ = invoker; }
296 template <
size_t I = 0>
void foreach_set_invoker_() noexcept {
297 if constexpr (I <
sizeof...(Handles)) {
298 auto *handle = std::get<I>(handles_);
299 auto &invoker = std::get<I>(child_invokers_);
300 invoker.self_ =
this;
301 handle->set_invoker(&invoker);
302 foreach_set_invoker_<I + 1>();
306 template <
size_t SkipIdx,
size_t I = 0>
307 void foreach_call_cancel_(Runtime *runtime)
noexcept {
308 if constexpr (I <
sizeof...(Handles)) {
309 auto handle = std::get<I>(handles_);
310 if constexpr (I != SkipIdx) {
311 handle->cancel(runtime);
313 foreach_call_cancel_<SkipIdx, I + 1>(runtime);
317 template <
size_t Idx>
void finish_() noexcept {
318 size_t no = finished_count_++;
321 if constexpr (Cancel) {
324 auto *runtime = detail::Context::current().runtime();
325 foreach_call_cancel_<Idx>(runtime);
329 if (no ==
sizeof...(Handles) - 1) {
331 assert(invoker_ !=
nullptr);
338 struct FinishInvoker :
public InvokerAdapter<FinishInvoker<I>> {
339 void invoke() noexcept { self_->template finish_<I>(); }
340 ParallelFinishHandle *self_;
343 template <
typename... input_t>
344 using tuple_cat_t =
decltype(std::tuple_cat(std::declval<input_t>()...));
346 template <
size_t I,
typename Arg,
typename... Args>
struct helper {
347 using type = tuple_cat_t<std::tuple<FinishInvoker<I>>,
348 typename helper<I + 1, Args...>::type>;
351 template <
size_t I,
typename Arg>
struct helper<I, Arg> {
352 using type = std::tuple<FinishInvoker<I>>;
355 using InvokerTupleType =
typename helper<0, Handles...>::type;
357 size_t finished_count_ = 0;
358 bool canceled_ =
false;
359 std::tuple<Handles *...> handles_;
360 InvokerTupleType child_invokers_;
361 std::array<size_t,
sizeof...(Handles)> order_;
362 Invoker *invoker_ =
nullptr;
365template <HandleLike... Handles>
366using ParallelAllFinishHandle = ParallelFinishHandle<
false, Handles...>;
368template <HandleLike... Handles>
369using ParallelAnyFinishHandle = ParallelFinishHandle<
true, Handles...>;
371template <HandleLike... Handles>
372class WhenAllFinishHandle :
public ParallelAllFinishHandle<Handles...> {
374 using Base = ParallelAllFinishHandle<Handles...>;
375 using ReturnType = std::tuple<
typename Handles::ReturnType...>;
377 ReturnType extract_result() noexcept(noexcept(Base::extract_result())) {
378 auto r = Base::extract_result();
379 return std::move(r.second);
383template <HandleLike... Handles>
384class WhenAnyFinishHandle :
public ParallelAnyFinishHandle<Handles...> {
386 using Base = ParallelAnyFinishHandle<Handles...>;
387 using ReturnType = std::variant<
typename Handles::ReturnType...>;
389 ReturnType extract_result() noexcept(noexcept(Base::extract_result())) {
390 auto r = Base::extract_result();
391 auto &[order, results] = r;
392 return tuple_at_(results, order[0]);
396 template <
size_t Idx = 0>
397 static auto tuple_at_(std::tuple<typename Handles::ReturnType...> &results,
399 if constexpr (Idx <
sizeof...(Handles)) {
401 return ReturnType{std::in_place_index<Idx>,
402 std::move(std::get<Idx>(results))};
404 return tuple_at_<Idx + 1>(results, idx);
409 assert(
false &&
"Index out of bounds");
410 return ReturnType{std::in_place_index<0>,
411 std::move(std::get<0>(results))};
Polymorphic invocation utilities.
The main namespace for the Condy library.
Runtime type for running the io_uring event loop.