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