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   96135 decode_io_result( 70   84191 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   96135 if (!ec_out) 78   84191 if (!ec_out)
MISUBC 79   return; 79   return;
HITCBC 80   96135 if (cancelled) 80   84191 if (cancelled)
HITCBC 81   366 *ec_out = capy::error::canceled; 81   357 *ec_out = capy::error::canceled;
HITCBC 82   95769 else if (err) 82   83834 else if (err)
HITCBC 83   25 *ec_out = err; 83   25 *ec_out = err;
HITCBC 84   95744 else if (is_read && bytes == 0 && !empty_buffer) 84   83809 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   95741 *ec_out = {}; 87   83806 *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   96135 coro_resume(coro_op* self) noexcept 125   84191 coro_resume(coro_op* self) noexcept
126   { 126   {
HITCBC 127   96135 self->cont_op.cont.h = self->h; 127   84191 self->cont_op.cont.h = self->h;
HITCBC 128   96135 auto next = dispatch_coro(self->ex, self->cont_op.cont); 128   84191 auto next = dispatch_coro(self->ex, self->cont_op.cont);
HITCBC 129   96135 auto suicide = std::move(self->impl_ptr); 129   84191 auto suicide = std::move(self->impl_ptr);
HITCBC 130   96135 next.resume(); 130   84191 next.resume();
131   // suicide drops here; may destroy impl + self. 131   // suicide drops here; may destroy impl + self.
HITCBC 132   96135 } 132   84191 }
133   133  
134   } // namespace boost::corosio::detail 134   } // namespace boost::corosio::detail
135   135  
136   #endif 136   #endif