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