include/boost/corosio/native/detail/coro_op.hpp

91.7% Lines (11/12) 80.0% List of functions (4/5)
coro_op.hpp
f(x) Functions (5)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_HPP
12
13 #include <boost/corosio/detail/config.hpp>
14 #include <boost/corosio/detail/continuation_op.hpp>
15 #include <boost/corosio/detail/scheduler_op.hpp>
16 #include <boost/capy/ex/executor_ref.hpp>
17
18 #include <atomic>
19 #include <coroutine>
20 #include <cstddef>
21 #include <memory>
22 #include <optional>
23 #include <stop_token>
24 #include <system_error>
25
26 /*
27 Shared, non-template op envelope for every native backend — the readiness
28 reactors (epoll/kqueue/select), io_uring, and IOCP. It captures the part of
29 an async operation that is identical regardless of how completion is
30 reported: the coroutine to resume, the executor it dispatches on, the
31 output pointers, the stop_token wiring, the cancelled flag, and the
32 keepalive that holds the owning impl alive while the op is in flight.
33
34 What is deliberately NOT here (it differs by backend and stays in the
35 derived op layer):
36 - the result model: the reactors re-run the syscall and record
37 `errn`/`bytes_transferred` (reactor_op_base); io_uring stores the raw
38 `res`/`cqe_flags`; IOCP stores `dwError`/`bytes_transferred`. Each
39 decodes its own result.
40 - the submission + the kernel cancel action. Cancellation is unified only
41 at the call site via the virtual `on_cancel()` hook: the stop_callback
42 always targets `coro_op`, and each backend overrides `on_cancel()` —
43 the reactors route to the owning impl's cancel(), io_uring submits an
44 ASYNC_CANCEL SQE, IOCP calls the stored cancel_func_/CancelIoEx.
45
46 See tasks/proactor-dedup-decisions.md and coro-op-unification-scope.md.
47 */
48
49 namespace boost::corosio::detail {
50
51 /** Non-template op envelope shared by every native backend's operations.
52
53 `reactor_op_base`, `io_uring_op`, and `overlapped_op` all derive from this.
54 Derives from scheduler_op so ops queue intrusively and dispatch through the
55 function-pointer (io_uring/IOCP) or virtual (reactors) completion path —
56 hence both a default and a func_type constructor.
57
58 @note For IOCP, the concrete op multiply-inherits `OVERLAPPED` as its
59 first base (so `static_cast<OVERLAPPED*>` round-trips); `coro_op`
60 follows it.
61 */
62 struct coro_op : scheduler_op
63 {
64 /** Stop-callback handler: routes a stop_token firing to `on_cancel()`.
65
66 A single canceller type for both backends keeps `stop_cb` (and thus
67 `start()`) in this shared base; the backend-specific action lives
68 behind the `on_cancel()` virtual.
69 */
70 struct canceller
71 {
72 coro_op* op;
73 214x void operator()() const noexcept { op->on_cancel(); }
74 };
75
76 std::coroutine_handle<> h;
77 detail::continuation_op cont_op;
78 capy::executor_ref ex;
79 std::error_code* ec_out = nullptr;
80 std::size_t* bytes_out = nullptr;
81
82 /// True for receive/read ops (drives the zero-byte == EOF decision).
83 bool is_read = false;
84 /// True when the submitted buffer was zero-length (suppresses EOF).
85 bool empty_buffer = false;
86
87 std::atomic<bool> cancelled{false};
88 std::optional<std::stop_callback<canceller>> stop_cb;
89
90 /// Keeps the owning impl alive while the op is in flight (the kernel
91 /// owns user buffers until completion). Dropped in the handler's resume
92 /// tail (see coro_op_complete.hpp).
93 std::shared_ptr<void> impl_ptr;
94
95 /// Default-construct for virtual-dispatch backends (the reactors, which
96 /// override operator()/destroy() and leave func_ null).
97 133100x coro_op() noexcept = default;
98
99 /// Construct with the completion function for func-pointer dispatch
100 /// (io_uring / IOCP completion handlers).
101 explicit coro_op(func_type func) noexcept : scheduler_op(func) {}
102
103 /** Arm the stop-token callback. Call before the op is submitted.
104
105 Resets the cancellation flag and (re)arms `stop_cb` against @a token.
106 Derived ops that carry extra pre-submit state (e.g. io_uring's
107 `sqe_set`) extend this.
108 */
109 102305x void start(std::stop_token const& token)
110 {
111 102305x cancelled.store(false, std::memory_order_relaxed);
112 102305x stop_cb.reset();
113 102305x if (token.stop_possible())
114 232x stop_cb.emplace(token, canceller{this});
115 102305x }
116
117 /// Mark this op cancellation-requested. Shared by every backend.
118 403786x void request_cancel() noexcept
119 {
120 403786x cancelled.store(true, std::memory_order_release);
121 403786x }
122
123 /** Backend cancellation hook, invoked when the stop_token fires.
124
125 The default just records the request. Backends override to also
126 drive the kernel: io_uring submits an ASYNC_CANCEL SQE; IOCP calls
127 its stored cancel_func_ (CancelIoEx / wait-reactor deregister).
128 */
129 virtual void on_cancel() noexcept { request_cancel(); }
130 };
131
132 } // namespace boost::corosio::detail
133
134 #endif
135