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

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