Condy v1.6.0
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
awaiters.hpp
Go to the documentation of this file.
1
9
10#pragma once
11
12#include "condy/concepts.hpp"
13#include "condy/condy_uring.hpp"
14#include "condy/context.hpp"
16#include "condy/ring.hpp"
17#include "condy/runtime.hpp"
18#include "condy/work_type.hpp"
19#include <coroutine>
20#include <cstddef>
21#include <memory>
22#include <tuple>
23
24namespace condy {
25
26template <OpFinishHandleLike Handle> class HandleBox {
27public:
28 HandleBox(Handle h) : handle_(std::move(h)) {}
29
30 Handle &get() noexcept { return handle_; }
31
32 void maybe_release() noexcept { /* No-op */ }
33
34private:
35 Handle handle_;
36};
37
38template <CQEHandlerLike CQEHandler, typename Func>
39class HandleBox<ZeroCopyOpFinishHandle<CQEHandler, Func>> {
40public:
41 using Handle = ZeroCopyOpFinishHandle<CQEHandler, Func>;
42 HandleBox(Handle h) : handle_ptr_(std::make_unique<Handle>(std::move(h))) {}
43 HandleBox(const HandleBox &other) // Deep copy
44 : handle_ptr_(std::make_unique<Handle>(*other.handle_ptr_)) {}
45
46 Handle &get() noexcept { return *handle_ptr_; }
47
48 void maybe_release() noexcept { handle_ptr_.release(); }
49
50private:
51 std::unique_ptr<Handle> handle_ptr_;
52};
53
54template <OpFinishHandleLike Handle, PrepFuncLike Func> class OpAwaiterBase {
55public:
56 using HandleType = Handle;
57
58 OpAwaiterBase(HandleBox<Handle> handle, Func func)
59 : prep_func_(func), finish_handle_(std::move(handle)) {}
60
61public:
62 HandleType *get_handle() noexcept { return &finish_handle_.get(); }
63
64 void init_finish_handle() noexcept { /* Leaf node, no-op */ }
65
66 void register_operation(unsigned int flags) noexcept {
67 auto &context = detail::Context::current();
68 auto *ring = context.ring();
69
70 context.runtime()->pend_work();
71
72 io_uring_sqe *sqe = prep_func_(ring);
73 assert(sqe && "prep_func must return a valid sqe");
74 io_uring_sqe_set_flags(sqe, sqe->flags | flags);
75 auto *work = encode_work(&finish_handle_.get(), WorkType::Common);
76 io_uring_sqe_set_data(sqe, work);
77 }
78
79public:
80 bool await_ready() const noexcept { return false; }
81
82 template <typename PromiseType>
83 void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
84 init_finish_handle();
85 finish_handle_.get().set_invoker(&h.promise());
86 register_operation(0);
87 }
88
89 auto await_resume() noexcept {
90 auto result = finish_handle_.get().extract_result();
91 finish_handle_.maybe_release();
92 return result;
93 }
94
95protected:
96 Func prep_func_;
97 HandleBox<Handle> finish_handle_;
98};
99
100template <PrepFuncLike PrepFunc, CQEHandlerLike CQEHandler>
101class [[nodiscard]] OpAwaiter
102 : public OpAwaiterBase<OpFinishHandle<CQEHandler>, PrepFunc> {
103public:
104 using Base = OpAwaiterBase<OpFinishHandle<CQEHandler>, PrepFunc>;
105 template <typename... Args>
106 OpAwaiter(PrepFunc func, Args &&...args)
107 : Base(HandleBox(
108 OpFinishHandle<CQEHandler>(std::forward<Args>(args)...)),
109 std::move(func)) {}
110};
111
112template <PrepFuncLike PrepFunc, CQEHandlerLike CQEHandler,
113 typename MultiShotFunc>
114class [[nodiscard]] MultiShotOpAwaiter
115 : public OpAwaiterBase<MultiShotOpFinishHandle<CQEHandler, MultiShotFunc>,
116 PrepFunc> {
117public:
118 using Base =
119 OpAwaiterBase<MultiShotOpFinishHandle<CQEHandler, MultiShotFunc>,
120 PrepFunc>;
121 template <typename... Args>
122 MultiShotOpAwaiter(PrepFunc func, MultiShotFunc multishot_func,
123 Args &&...args)
124 : Base(HandleBox(MultiShotOpFinishHandle<CQEHandler, MultiShotFunc>(
125 std::move(multishot_func), std::forward<Args>(args)...)),
126 std::move(func)) {}
127};
128
129template <PrepFuncLike PrepFunc, CQEHandlerLike CQEHandler, typename FreeFunc>
130class [[nodiscard]] ZeroCopyOpAwaiter
131 : public OpAwaiterBase<ZeroCopyOpFinishHandle<CQEHandler, FreeFunc>,
132 PrepFunc> {
133public:
134 using Base =
135 OpAwaiterBase<ZeroCopyOpFinishHandle<CQEHandler, FreeFunc>, PrepFunc>;
136 template <typename... Args>
137 ZeroCopyOpAwaiter(PrepFunc func, FreeFunc free_func, Args &&...args)
138 : Base(HandleBox(ZeroCopyOpFinishHandle<CQEHandler, FreeFunc>(
139 std::move(free_func), std::forward<Args>(args)...)),
140 std::move(func)) {}
141};
142
143template <unsigned int Flags, AwaiterLike Awaiter>
144class [[nodiscard]] FlaggedOpAwaiter : public Awaiter {
145public:
146 using Base = Awaiter;
147 FlaggedOpAwaiter(Awaiter awaiter) : Base(std::move(awaiter)) {}
148
149 void register_operation(unsigned int flags) noexcept {
150 Base::register_operation(flags | Flags);
151 }
152
153 template <typename PromiseType>
154 void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
155 Base::init_finish_handle();
156 Base::get_handle()->set_invoker(&h.promise());
157 register_operation(0);
158 }
159};
160
161template <HandleLike Handle, AwaiterLike Awaiter>
162class [[nodiscard]] RangedParallelAwaiterBase {
163public:
164 using HandleType = Handle;
165
166 RangedParallelAwaiterBase(std::vector<Awaiter> awaiters)
167 : awaiters_(std::move(awaiters)) {}
168
169public:
170 HandleType *get_handle() noexcept { return &finish_handle_; }
171
172 void init_finish_handle() noexcept {
173 using ChildHandle = typename Awaiter::HandleType;
174 std::vector<ChildHandle *> handles;
175 handles.reserve(awaiters_.size());
176 for (auto &awaiter : awaiters_) {
177 awaiter.init_finish_handle();
178 handles.push_back(awaiter.get_handle());
179 }
180 finish_handle_.init(std::move(handles));
181 }
182
183 void register_operation(unsigned int flags) noexcept {
184 for (auto &awaiter : awaiters_) {
185 awaiter.register_operation(flags);
186 }
187 }
188
189public:
190 bool await_ready() const noexcept { return awaiters_.empty(); }
191
192 template <typename PromiseType>
193 void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
194 init_finish_handle();
195 finish_handle_.set_invoker(&h.promise());
196 register_operation(0);
197 }
198
199 typename Handle::ReturnType
200 await_resume() noexcept(is_nothrow_extract_result_v<Handle>) {
201 return finish_handle_.extract_result();
202 }
203
204public:
205 void push(Awaiter awaiter) { awaiters_.push_back(std::move(awaiter)); }
206
207protected:
208 HandleType finish_handle_;
209 std::vector<Awaiter> awaiters_;
210};
211
221template <typename Awaiter>
222using RangedParallelAllAwaiter = RangedParallelAwaiterBase<
223 RangedParallelAllFinishHandle<typename Awaiter::HandleType>, Awaiter>;
224
234template <typename Awaiter>
235using RangedParallelAnyAwaiter = RangedParallelAwaiterBase<
236 RangedParallelAnyFinishHandle<typename Awaiter::HandleType>, Awaiter>;
237
244template <typename Awaiter>
245using RangedWhenAllAwaiter = RangedParallelAwaiterBase<
246 RangedWhenAllFinishHandle<typename Awaiter::HandleType>, Awaiter>;
247
256template <typename Awaiter>
257using RangedWhenAnyAwaiter = RangedParallelAwaiterBase<
258 RangedWhenAnyFinishHandle<typename Awaiter::HandleType>, Awaiter>;
259
260template <unsigned int Flags, AwaiterLike Awaiter>
261class [[nodiscard]] RangedLinkAwaiterBase
262 : public RangedWhenAllAwaiter<Awaiter> {
263public:
265 using Base::Base;
266
267 void register_operation(unsigned int flags) noexcept {
268 auto *ring = detail::Context::current().ring();
269 ring->reserve_space(Base::awaiters_.size());
270 for (int i = 0; i < Base::awaiters_.size() - 1; ++i) {
271 Base::awaiters_[i].register_operation(flags | Flags);
272 }
273 Base::awaiters_.back().register_operation(flags);
274 }
275
276 template <typename PromiseType>
277 void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
278 Base::init_finish_handle();
279 Base::finish_handle_.set_invoker(&h.promise());
280 register_operation(0);
281 }
282};
283
290template <typename Awaiter>
291using RangedLinkAwaiter = RangedLinkAwaiterBase<IOSQE_IO_LINK, Awaiter>;
292
300template <typename Awaiter>
301using RangedHardLinkAwaiter = RangedLinkAwaiterBase<IOSQE_IO_HARDLINK, Awaiter>;
302
303template <HandleLike Handle, AwaiterLike... Awaiters>
304class [[nodiscard]] ParallelAwaiterBase {
305public:
306 using HandleType = Handle;
307
308 ParallelAwaiterBase(Awaiters... awaiters)
309 : awaiters_(std::move(awaiters)...) {}
310 template <typename ParallelAwaiter, AwaiterLike New>
311 ParallelAwaiterBase(ParallelAwaiter &&aws, New &&new_awaiter)
312 : awaiters_(
313 std::tuple_cat(std::move(aws.awaiters_),
314 std::make_tuple(std::forward<New>(new_awaiter)))) {
315 }
316
317public:
318 HandleType *get_handle() noexcept { return &finish_handle_; }
319
320 void init_finish_handle() noexcept {
321 std::apply(
322 [this](auto &&...awaiters) {
323 (awaiters.init_finish_handle(), ...);
324 finish_handle_.init(awaiters.get_handle()...);
325 },
326 awaiters_);
327 }
328
329 void register_operation(unsigned int flags) noexcept {
330 std::apply(
331 [flags](auto &&...awaiters) {
332 (awaiters.register_operation(flags), ...);
333 },
334 awaiters_);
335 }
336
337public:
338 bool await_ready() const noexcept { return false; }
339
340 template <typename PromiseType>
341 void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
342 init_finish_handle();
343 finish_handle_.set_invoker(&h.promise());
344 register_operation(0);
345 }
346
347 typename Handle::ReturnType
348 await_resume() noexcept(is_nothrow_extract_result_v<Handle>) {
349 return finish_handle_.extract_result();
350 }
351
352protected:
353 HandleType finish_handle_;
354 std::tuple<Awaiters...> awaiters_;
355
356 // Make awaiters_ accessible to all template instantiations
357 template <HandleLike, AwaiterLike...> friend class ParallelAwaiterBase;
358};
359
369template <typename... Awaiter>
370using ParallelAllAwaiter = ParallelAwaiterBase<
371 ParallelAllFinishHandle<typename Awaiter::HandleType...>, Awaiter...>;
372
382template <typename... Awaiter>
383using ParallelAnyAwaiter = ParallelAwaiterBase<
384 ParallelAnyFinishHandle<typename Awaiter::HandleType...>, Awaiter...>;
385
392template <typename... Awaiter>
394 ParallelAwaiterBase<WhenAllFinishHandle<typename Awaiter::HandleType...>,
395 Awaiter...>;
396
404template <typename... Awaiter>
406 ParallelAwaiterBase<WhenAnyFinishHandle<typename Awaiter::HandleType...>,
407 Awaiter...>;
408
409template <unsigned int Flags, AwaiterLike... Awaiter>
410class [[nodiscard]] LinkAwaiterBase : public WhenAllAwaiter<Awaiter...> {
411public:
412 using Base = WhenAllAwaiter<Awaiter...>;
413 using Base::Base;
414
415 void register_operation(unsigned int flags) noexcept {
416 auto *ring = detail::Context::current().ring();
417 ring->reserve_space(sizeof...(Awaiter));
418 foreach_register_operation_(flags);
419 }
420
421 template <typename PromiseType>
422 void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
423 Base::init_finish_handle();
424 Base::finish_handle_.set_invoker(&h.promise());
425 register_operation(0);
426 }
427
428private:
429 template <size_t Idx = 0>
430 void foreach_register_operation_(unsigned int flags) noexcept {
431 if constexpr (Idx < sizeof...(Awaiter)) {
432 auto &awaiter = std::get<Idx>(Base::awaiters_);
433 if constexpr (Idx < sizeof...(Awaiter) - 1) {
434 awaiter.register_operation(flags | Flags);
435 } else {
436 awaiter.register_operation(flags);
437 }
438 foreach_register_operation_<Idx + 1>(flags);
439 }
440 }
441};
442
449template <typename... Awaiter>
450using LinkAwaiter = LinkAwaiterBase<IOSQE_IO_LINK, Awaiter...>;
451
458template <typename... Awaiter>
459using HardLinkAwaiter = LinkAwaiterBase<IOSQE_IO_HARDLINK, Awaiter...>;
460
461} // namespace condy
Definitions of finish handle types for asynchronous operations.
The main namespace for the Condy library.
Definition condy.hpp:30
RangedParallelAwaiterBase< RangedParallelAllFinishHandle< typename Awaiter::HandleType >, Awaiter > RangedParallelAllAwaiter
Awaiter to wait for all operations in a range to complete.
Definition awaiters.hpp:222
ParallelAwaiterBase< ParallelAllFinishHandle< typename Awaiter::HandleType... >, Awaiter... > ParallelAllAwaiter
Awaiter to wait for all operations to complete in parallel.
Definition awaiters.hpp:370
RangedLinkAwaiterBase< IOSQE_IO_LINK, Awaiter > RangedLinkAwaiter
Awaiter that links multiple operations in a range using IO_LINK.
Definition awaiters.hpp:291
RangedParallelAwaiterBase< RangedWhenAnyFinishHandle< typename Awaiter::HandleType >, Awaiter > RangedWhenAnyAwaiter
Awaiter to wait for any operation in a range to complete.
Definition awaiters.hpp:257
RangedParallelAwaiterBase< RangedParallelAnyFinishHandle< typename Awaiter::HandleType >, Awaiter > RangedParallelAnyAwaiter
Awaiter to wait for any operation in a range to complete.
Definition awaiters.hpp:235
LinkAwaiterBase< IOSQE_IO_LINK, Awaiter... > LinkAwaiter
Awaiter that links multiple operations using IO_LINK.
Definition awaiters.hpp:450
RangedParallelAwaiterBase< RangedWhenAllFinishHandle< typename Awaiter::HandleType >, Awaiter > RangedWhenAllAwaiter
Awaiter to wait for all operations in a range to complete.
Definition awaiters.hpp:245
ParallelAwaiterBase< WhenAllFinishHandle< typename Awaiter::HandleType... >, Awaiter... > WhenAllAwaiter
Awaiter that waits for all operations to complete in parallel.
Definition awaiters.hpp:393
ParallelAwaiterBase< WhenAnyFinishHandle< typename Awaiter::HandleType... >, Awaiter... > WhenAnyAwaiter
Awaiter that waits for any operation to complete in parallel.
Definition awaiters.hpp:405
ParallelAwaiterBase< ParallelAnyFinishHandle< typename Awaiter::HandleType... >, Awaiter... > ParallelAnyAwaiter
Awaiter to wait for any operation to complete in parallel.
Definition awaiters.hpp:383
LinkAwaiterBase< IOSQE_IO_HARDLINK, Awaiter... > HardLinkAwaiter
Awaiter that links multiple operations using IO_HARDLINK.
Definition awaiters.hpp:459
RangedLinkAwaiterBase< IOSQE_IO_HARDLINK, Awaiter > RangedHardLinkAwaiter
Awaiter that links multiple operations in a range using IO_HARDLINK.
Definition awaiters.hpp:301
Wrapper classes for liburing interfaces.
Runtime type for running the io_uring event loop.