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

100.0% Lines (16/0/16) 100.0% List of functions (5/0/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/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`, `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 407x void operator()() const noexcept
74 {
75 407x op->on_cancel();
76 407x }
77 };
78
79 std::coroutine_handle<> h;
80 capy::continuation cont;
81 capy::executor_ref ex;
82 std::error_code* ec_out = nullptr;
83 std::size_t* bytes_out = nullptr;
84
85 /// True for receive/read ops (drives the zero-byte == EOF decision).
86 bool is_read = false;
87 /// True when the submitted buffer was zero-length (suppresses EOF).
88 bool empty_buffer = false;
89
90 std::atomic<bool> cancelled{false};
91 std::optional<std::stop_callback<canceller>> stop_cb;
92
93 /// Keeps the owning impl alive while the op is in flight (the kernel
94 /// owns user buffers until completion). Dropped in the handler's resume
95 /// tail (see coro_op_complete.hpp).
96 std::shared_ptr<void> impl_ptr;
97
98 /// Default-construct for virtual-dispatch backends (the reactors, which
99 /// override operator()/destroy() and leave func_ null).
100 95654x coro_op() noexcept = default;
101
102 /// Construct with the completion function for func-pointer dispatch
103 /// (io_uring / IOCP completion handlers).
104 explicit coro_op(func_type func) noexcept : scheduler_op(func) {}
105
106 /** Arm the stop-token callback. Call before the op is submitted.
107
108 Resets the cancellation flag and (re)arms `stop_cb` against @a token.
109 Derived ops that carry extra pre-submit state (e.g. io_uring's
110 `sqe_set`) extend this.
111 */
112 92193x void start(std::stop_token const& token)
113 {
114 92193x cancelled.store(false, std::memory_order_relaxed);
115 92193x stop_cb.reset();
116 92193x if (token.stop_possible())
117 517x stop_cb.emplace(token, canceller{this});
118 92193x }
119
120 /// Mark this op cancellation-requested. Shared by every backend.
121 288223x void request_cancel() noexcept
122 {
123 288223x cancelled.store(true, std::memory_order_release);
124 288223x }
125
126 /** Backend cancellation hook, invoked when the stop_token fires.
127
128 The default just records the request. Backends override to also
129 drive the kernel: io_uring submits an ASYNC_CANCEL SQE; IOCP calls
130 its stored cancel_func_ (CancelIoEx / wait-reactor deregister).
131 */
132 20x virtual void on_cancel() noexcept
133 {
134 20x request_cancel();
135 20x }
136 };
137
138 } // namespace boost::corosio::detail
139
140 #endif
141