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