93.75% Lines (15/16) 100.00% Functions (2/2)
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_COMPLETE_HPP 10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_COMPLETE_HPP
11   #define BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_COMPLETE_HPP 11   #define BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_COMPLETE_HPP
12   12  
13   #include <boost/corosio/detail/dispatch_coro.hpp> 13   #include <boost/corosio/detail/dispatch_coro.hpp>
14   #include <boost/corosio/native/detail/coro_op.hpp> 14   #include <boost/corosio/native/detail/coro_op.hpp>
15   #include <boost/capy/error.hpp> 15   #include <boost/capy/error.hpp>
16   16  
17   #include <cstddef> 17   #include <cstddef>
18   #include <memory> 18   #include <memory>
19   #include <system_error> 19   #include <system_error>
20   20  
21   /* 21   /*
22   Shared completion-tail helpers for proactor ops. Every IOCP and io_uring 22   Shared completion-tail helpers for proactor ops. Every IOCP and io_uring
23   I/O handler ends the same way once its backend-specific result has been 23   I/O handler ends the same way once its backend-specific result has been
24   decoded into ec_out/bytes_out: 24   decoded into ec_out/bytes_out:
25   25  
26   1. disarm the stop_callback, 26   1. disarm the stop_callback,
27   2. on the shutdown-drain path (owner == nullptr) just break the 27   2. on the shutdown-drain path (owner == nullptr) just break the
28   impl_ptr keepalive cycle and return without resuming, 28   impl_ptr keepalive cycle and return without resuming,
29   3. otherwise resume the coroutine on its executor, dropping the 29   3. otherwise resume the coroutine on its executor, dropping the
30   keepalive only after the continuation has been handed off. 30   keepalive only after the continuation has been handed off.
31   31  
32   The *decode* step (raw DWORD/res -> {ec, bytes, eof, canceled}) stays 32   The *decode* step (raw DWORD/res -> {ec, bytes, eof, canceled}) stays
33   backend-specific because the raw encodings differ; in Phase 3 it is 33   backend-specific because the raw encodings differ; in Phase 3 it is
34   formalized as `Traits::decode_result`. These two helpers capture the 34   formalized as `Traits::decode_result`. These two helpers capture the
35   backend-agnostic prologue and resume tail so the per-op handlers shrink to 35   backend-agnostic prologue and resume tail so the per-op handlers shrink to
36   "drain-or-decode, then resume". 36   "drain-or-decode, then resume".
37   */ 37   */
38   38  
39   namespace boost::corosio::detail { 39   namespace boost::corosio::detail {
40   40  
41   /** Translate a decoded I/O result into `*ec_out` using the cancelled / 41   /** Translate a decoded I/O result into `*ec_out` using the cancelled /
42   error / EOF / success priority shared by every native backend. 42   error / EOF / success priority shared by every native backend.
43   43  
44   The raw error encodings differ per backend (reactor positive `errno`, 44   The raw error encodings differ per backend (reactor positive `errno`,
45   io_uring negative `res`, IOCP `DWORD`), so the native-error -> error_code 45   io_uring negative `res`, IOCP `DWORD`), so the native-error -> error_code
46   step stays backend-local: the caller passes @a err already converted 46   step stays backend-local: the caller passes @a err already converted
47   (an empty error_code means "no error"). This helper owns only the 47   (an empty error_code means "no error"). This helper owns only the
48   priority logic, which is byte-for-byte identical everywhere: 48   priority logic, which is byte-for-byte identical everywhere:
49   49  
50   cancelled -> operation_canceled 50   cancelled -> operation_canceled
51   err set -> err 51   err set -> err
52   is_read && bytes == 0 && !empty -> end_of_file 52   is_read && bytes == 0 && !empty -> end_of_file
53   otherwise -> success 53   otherwise -> success
54   54  
55   Writes nothing when @a ec_out is null. Does not touch bytes_out — callers 55   Writes nothing when @a ec_out is null. Does not touch bytes_out — callers
56   that report a byte count write it separately (connect/wait carry none). 56   that report a byte count write it separately (connect/wait carry none).
57   57  
58   @param ec_out Destination (may be null). 58   @param ec_out Destination (may be null).
59   @param cancelled The op's cancellation flag. 59   @param cancelled The op's cancellation flag.
60   @param err Backend error already converted to error_code, or a 60   @param err Backend error already converted to error_code, or a
61   default-constructed error_code on success. 61   default-constructed error_code on success.
62   @param is_read True only for reads that should map a 0-byte 62   @param is_read True only for reads that should map a 0-byte
63   completion to EOF — false for writes, connect, wait, 63   completion to EOF — false for writes, connect, wait,
64   and datagrams (a 0-byte datagram is success, not EOF). 64   and datagrams (a 0-byte datagram is success, not EOF).
65   @param bytes Bytes transferred (consulted only for the EOF test). 65   @param bytes Bytes transferred (consulted only for the EOF test).
66   @param empty_buffer True when the submitted buffer was zero-length, 66   @param empty_buffer True when the submitted buffer was zero-length,
67   which suppresses the otherwise-spurious EOF. 67   which suppresses the otherwise-spurious EOF.
68   */ 68   */
69   inline void 69   inline void
HITCBC 70   126773 decode_io_result( 70   100874 decode_io_result(
71   std::error_code* ec_out, 71   std::error_code* ec_out,
72   bool cancelled, 72   bool cancelled,
73   std::error_code err, 73   std::error_code err,
74   bool is_read, 74   bool is_read,
75   std::size_t bytes, 75   std::size_t bytes,
76   bool empty_buffer) noexcept 76   bool empty_buffer) noexcept
77   { 77   {
HITCBC 78   126773 if (!ec_out) 78   100874 if (!ec_out)
MISUBC 79   return; 79   return;
HITCBC 80   126773 if (cancelled) 80   100874 if (cancelled)
HITCBC 81   382 *ec_out = capy::error::canceled; 81   372 *ec_out = capy::error::canceled;
HITCBC 82   126391 else if (err) 82   100502 else if (err)
HITCBC 83   24 *ec_out = err; 83   25 *ec_out = err;
HITCBC 84   126367 else if (is_read && bytes == 0 && !empty_buffer) 84   100477 else if (is_read && bytes == 0 && !empty_buffer)
HITCBC 85   3 *ec_out = capy::error::eof; 85   3 *ec_out = capy::error::eof;
86   else 86   else
HITCBC 87   126364 *ec_out = {}; 87   100474 *ec_out = {};
88   } 88   }
89   89  
90   /** Completion prologue shared by every proactor handler. 90   /** Completion prologue shared by every proactor handler.
91   91  
92   Disarms the stop_callback, then detects the shutdown-drain path. 92   Disarms the stop_callback, then detects the shutdown-drain path.
93   93  
94   @param owner The scheduler pointer (nullptr during shutdown drain). 94   @param owner The scheduler pointer (nullptr during shutdown drain).
95   @param self The completing op. 95   @param self The completing op.
96   @return True if this was a shutdown drain — the caller must `return` 96   @return True if this was a shutdown drain — the caller must `return`
97   immediately without decoding or resuming. On that path the 97   immediately without decoding or resuming. On that path the
98   impl_ptr keepalive is dropped here (which may destroy the impl, 98   impl_ptr keepalive is dropped here (which may destroy the impl,
99   and with it the op storage). 99   and with it the op storage).
100   */ 100   */
101   inline bool 101   inline bool
102   coro_drain_if_shutdown(void* owner, coro_op* self) noexcept 102   coro_drain_if_shutdown(void* owner, coro_op* self) noexcept
103   { 103   {
104   self->stop_cb.reset(); 104   self->stop_cb.reset();
105   if (owner == nullptr) 105   if (owner == nullptr)
106   { 106   {
107   auto suicide = std::move(self->impl_ptr); 107   auto suicide = std::move(self->impl_ptr);
108   return true; 108   return true;
109   } 109   }
110   return false; 110   return false;
111   } 111   }
112   112  
113   /** Resume tail shared by every proactor handler. 113   /** Resume tail shared by every proactor handler.
114   114  
115   Resumes the op's coroutine on its executor and then drops the impl_ptr 115   Resumes the op's coroutine on its executor and then drops the impl_ptr
116   keepalive. The keepalive is moved into a local that is released *after* 116   keepalive. The keepalive is moved into a local that is released *after*
117   `resume()` returns, matching the existing io_uring ordering: the impl (and 117   `resume()` returns, matching the existing io_uring ordering: the impl (and
118   therefore this op's storage) may be destroyed as the local goes out of 118   therefore this op's storage) may be destroyed as the local goes out of
119   scope, so nothing may touch `*self` after the resume. 119   scope, so nothing may touch `*self` after the resume.
120   120  
121   @pre `self->ec_out`/`bytes_out` have already been written by the 121   @pre `self->ec_out`/`bytes_out` have already been written by the
122   backend's decode step. 122   backend's decode step.
123   */ 123   */
124   inline void 124   inline void
HITCBC 125   126773 coro_resume(coro_op* self) noexcept 125   100874 coro_resume(coro_op* self) noexcept
126   { 126   {
HITCBC 127   126773 self->cont_op.cont.h = self->h; 127   100874 self->cont_op.cont.h = self->h;
HITCBC 128   126773 auto next = dispatch_coro(self->ex, self->cont_op.cont); 128   100874 auto next = dispatch_coro(self->ex, self->cont_op.cont);
HITCBC 129   126773 auto suicide = std::move(self->impl_ptr); 129   100874 auto suicide = std::move(self->impl_ptr);
HITCBC 130   126773 next.resume(); 130   100874 next.resume();
131   // suicide drops here; may destroy impl + self. 131   // suicide drops here; may destroy impl + self.
HITCBC 132   126773 } 132   100874 }
133   133  
134   } // namespace boost::corosio::detail 134   } // namespace boost::corosio::detail
135   135  
136   #endif 136   #endif