LCOV - code coverage report
Current view: top level - corosio/native/detail - coro_op_complete.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 93.8 % 16 15 1
Test Date: 2026-06-09 16:09:19 Functions: 100.0 % 2 2

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Michael Vandeberg
       3                 : //
       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)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/corosio
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_COMPLETE_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_CORO_OP_COMPLETE_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/dispatch_coro.hpp>
      14                 : #include <boost/corosio/native/detail/coro_op.hpp>
      15                 : #include <boost/capy/error.hpp>
      16                 : 
      17                 : #include <cstddef>
      18                 : #include <memory>
      19                 : #include <system_error>
      20                 : 
      21                 : /*
      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
      24                 :     decoded into ec_out/bytes_out:
      25                 : 
      26                 :       1. disarm the stop_callback,
      27                 :       2. on the shutdown-drain path (owner == nullptr) just break the
      28                 :          impl_ptr keepalive cycle and return without resuming,
      29                 :       3. otherwise resume the coroutine on its executor, dropping the
      30                 :          keepalive only after the continuation has been handed off.
      31                 : 
      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
      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
      36                 :     "drain-or-decode, then resume".
      37                 : */
      38                 : 
      39                 : namespace boost::corosio::detail {
      40                 : 
      41                 : /** Translate a decoded I/O result into `*ec_out` using the cancelled /
      42                 :     error / EOF / success priority shared by every native backend.
      43                 : 
      44                 :     The raw error encodings differ per backend (reactor positive `errno`,
      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
      47                 :     (an empty error_code means "no error"). This helper owns only the
      48                 :     priority logic, which is byte-for-byte identical everywhere:
      49                 : 
      50                 :         cancelled                          -> operation_canceled
      51                 :         err set                            -> err
      52                 :         is_read && bytes == 0 && !empty    -> end_of_file
      53                 :         otherwise                          -> success
      54                 : 
      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).
      57                 : 
      58                 :     @param ec_out        Destination (may be null).
      59                 :     @param cancelled     The op's cancellation flag.
      60                 :     @param err           Backend error already converted to error_code, or a
      61                 :                          default-constructed error_code on success.
      62                 :     @param is_read       True only for reads that should map a 0-byte
      63                 :                          completion to EOF — false for writes, connect, wait,
      64                 :                          and datagrams (a 0-byte datagram is success, not EOF).
      65                 :     @param bytes         Bytes transferred (consulted only for the EOF test).
      66                 :     @param empty_buffer  True when the submitted buffer was zero-length,
      67                 :                          which suppresses the otherwise-spurious EOF.
      68                 : */
      69                 : inline void
      70 HIT      102301 : decode_io_result(
      71                 :     std::error_code* ec_out,
      72                 :     bool             cancelled,
      73                 :     std::error_code  err,
      74                 :     bool             is_read,
      75                 :     std::size_t      bytes,
      76                 :     bool             empty_buffer) noexcept
      77                 : {
      78          102301 :     if (!ec_out)
      79 MIS           0 :         return;
      80 HIT      102301 :     if (cancelled)
      81             370 :         *ec_out = capy::error::canceled;
      82          101931 :     else if (err)
      83              25 :         *ec_out = err;
      84          101906 :     else if (is_read && bytes == 0 && !empty_buffer)
      85               3 :         *ec_out = capy::error::eof;
      86                 :     else
      87          101903 :         *ec_out = {};
      88                 : }
      89                 : 
      90                 : /** Completion prologue shared by every proactor handler.
      91                 : 
      92                 :     Disarms the stop_callback, then detects the shutdown-drain path.
      93                 : 
      94                 :     @param owner The scheduler pointer (nullptr during shutdown drain).
      95                 :     @param self  The completing op.
      96                 :     @return True if this was a shutdown drain — the caller must `return`
      97                 :             immediately without decoding or resuming. On that path the
      98                 :             impl_ptr keepalive is dropped here (which may destroy the impl,
      99                 :             and with it the op storage).
     100                 : */
     101                 : inline bool
     102                 : coro_drain_if_shutdown(void* owner, coro_op* self) noexcept
     103                 : {
     104                 :     self->stop_cb.reset();
     105                 :     if (owner == nullptr)
     106                 :     {
     107                 :         auto suicide = std::move(self->impl_ptr);
     108                 :         return true;
     109                 :     }
     110                 :     return false;
     111                 : }
     112                 : 
     113                 : /** Resume tail shared by every proactor handler.
     114                 : 
     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*
     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
     119                 :     scope, so nothing may touch `*self` after the resume.
     120                 : 
     121                 :     @pre `self->ec_out`/`bytes_out` have already been written by the
     122                 :          backend's decode step.
     123                 : */
     124                 : inline void
     125          102301 : coro_resume(coro_op* self) noexcept
     126                 : {
     127          102301 :     self->cont_op.cont.h = self->h;
     128          102301 :     auto next = dispatch_coro(self->ex, self->cont_op.cont);
     129          102301 :     auto suicide = std::move(self->impl_ptr);
     130          102301 :     next.resume();
     131                 :     // suicide drops here; may destroy impl + self.
     132          102301 : }
     133                 : 
     134                 : } // namespace boost::corosio::detail
     135                 : 
     136                 : #endif
        

Generated by: LCOV version 2.3