LCOV - code coverage report
Current view: top level - corosio/native/detail - coro_op.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 91.7 % 12 11 1
Test Date: 2026-06-09 16:09:19 Functions: 80.0 % 5 4 1

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

Generated by: LCOV version 2.3