LCOV - code coverage report
Current view: top level - corosio/native/detail/reactor - reactor_op_complete.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 97.1 % 104 101 3
Test Date: 2026-08-17 12:51:06 Functions: 100.0 % 40 40

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       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_REACTOR_REACTOR_OP_COMPLETE_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_OP_COMPLETE_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/dispatch_coro.hpp>
      14                 : #include <boost/corosio/native/detail/coro_op_complete.hpp>
      15                 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
      16                 : #include <boost/corosio/native/detail/make_err.hpp>
      17                 : #include <boost/corosio/io/io_object.hpp>
      18                 : 
      19                 : #include <coroutine>
      20                 : #include <mutex>
      21                 : #include <utility>
      22                 : 
      23                 : #include <netinet/in.h>
      24                 : #include <sys/socket.h>
      25                 : #include <unistd.h>
      26                 : 
      27                 : namespace boost::corosio::detail {
      28                 : 
      29                 : /** Complete a base read/write operation.
      30                 : 
      31                 :     Translates the recorded errno and cancellation state into
      32                 :     an error_code, stores the byte count, then resumes the
      33                 :     caller via symmetric transfer.
      34                 : 
      35                 :     @tparam Op The concrete operation type.
      36                 :     @param op The operation to complete.
      37                 : */
      38                 : template<typename Op>
      39                 : void
      40 HIT      137478 : complete_io_op(Op& op)
      41                 : {
      42          137478 :     op.stop_cb.reset();
      43          137478 :     op.socket_impl_->desc_state_.scheduler_->reset_inline_budget();
      44                 : 
      45                 :     // is_read_operation() already folds in the empty-buffer case (it
      46                 :     // returns false for a zero-length read), so empty_buffer stays false
      47                 :     // here and the shared EOF test reduces to the reactor's original
      48                 :     // `is_read && bytes == 0`.
      49          274949 :     decode_io_result(
      50                 :         op.ec_out,
      51          137478 :         op.cancelled.load(std::memory_order_acquire),
      52          137478 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
      53          137478 :         op.is_read_operation(), op.bytes_transferred, /*empty_buffer=*/false);
      54                 : 
      55          137478 :     *op.bytes_out = op.bytes_transferred;
      56                 : 
      57          137478 :     coro_resume(&op);
      58          137478 : }
      59                 : 
      60                 : /** Complete a datagram recv operation (connected mode).
      61                 : 
      62                 :     Like complete_io_op but does not translate zero bytes into
      63                 :     EOF. Zero-length datagrams are valid and should be reported
      64                 :     as success with 0 bytes transferred.
      65                 : 
      66                 :     @param op The operation to complete.
      67                 : */
      68                 : template<typename Op>
      69                 : void
      70                 : complete_dgram_recv_op(Op& op)
      71                 : {
      72                 :     op.stop_cb.reset();
      73                 :     op.socket_impl_->desc_state_.scheduler_->reset_inline_budget();
      74                 : 
      75                 :     // No EOF: a zero-length datagram is valid (success with 0 bytes).
      76                 :     decode_io_result(
      77                 :         op.ec_out,
      78                 :         op.cancelled.load(std::memory_order_acquire),
      79                 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
      80                 :         /*is_read=*/false, /*bytes=*/0, /*empty_buffer=*/false);
      81                 : 
      82                 :     *op.bytes_out = op.bytes_transferred;
      83                 : 
      84                 :     coro_resume(&op);
      85                 : }
      86                 : 
      87                 : /** Complete a wait operation.
      88                 : 
      89                 :     Wait operations report only an error_code — no bytes_transferred,
      90                 :     no EOF translation. Used for socket and acceptor wait() awaitables;
      91                 :     picks the impl pointer set by start() to reach the scheduler.
      92                 : 
      93                 :     @tparam Op The concrete wait operation type.
      94                 :     @param op The operation to complete.
      95                 : */
      96                 : template<typename Op>
      97                 : void
      98              88 : complete_wait_op(Op& op)
      99                 : {
     100              88 :     op.stop_cb.reset();
     101                 :     // scheduler_ is null until the descriptor is registered; a wait
     102                 :     // completed by the initiation probe (e.g. EBADF on a never-opened
     103                 :     // socket) has no registration to reset a budget for.
     104              88 :     if (op.socket_impl_)
     105                 :     {
     106              71 :         if (auto* sched = op.socket_impl_->desc_state_.scheduler_)
     107              69 :             sched->reset_inline_budget();
     108                 :     }
     109              17 :     else if (auto* sched = op.acceptor_impl_->desc_state_.scheduler_)
     110                 :     {
     111              17 :         sched->reset_inline_budget();
     112                 :     }
     113                 : 
     114                 :     // Wait reports only success/cancel/error — no bytes, no EOF.
     115             174 :     decode_io_result(
     116                 :         op.ec_out,
     117              88 :         op.cancelled.load(std::memory_order_acquire),
     118              88 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
     119                 :         /*is_read=*/false, /*bytes=*/0, /*empty_buffer=*/false);
     120                 : 
     121              88 :     coro_resume(&op);
     122              88 : }
     123                 : 
     124                 : /** Complete a connect operation with endpoint caching.
     125                 : 
     126                 :     On success, queries the local endpoint via getsockname and
     127                 :     caches both endpoints in the socket impl. Then resumes the
     128                 :     caller via symmetric transfer.
     129                 : 
     130                 :     @tparam Op The concrete connect operation type.
     131                 :     @param op The operation to complete.
     132                 : */
     133                 : template<typename Op>
     134                 : void
     135            8607 : complete_connect_op(Op& op)
     136                 : {
     137            8607 :     op.stop_cb.reset();
     138            8607 :     op.socket_impl_->desc_state_.scheduler_->reset_inline_budget();
     139                 : 
     140            8607 :     bool success =
     141            8607 :         (op.errn == 0 && !op.cancelled.load(std::memory_order_acquire));
     142                 : 
     143            8607 :     if (success && op.socket_impl_)
     144                 :     {
     145                 :         using ep_type = decltype(op.target_endpoint);
     146            8564 :         ep_type local_ep;
     147            8564 :         sockaddr_storage local_storage{};
     148            8564 :         socklen_t local_len = sizeof(local_storage);
     149            8564 :         if (::getsockname(
     150                 :                 op.fd, reinterpret_cast<sockaddr*>(&local_storage),
     151            8564 :                 &local_len) == 0)
     152            8543 :             local_ep =
     153            8564 :                 from_sockaddr_as(local_storage, local_len, ep_type{});
     154            8564 :         op.socket_impl_->set_endpoints(local_ep, op.target_endpoint);
     155                 :     }
     156                 : 
     157           17182 :     decode_io_result(
     158                 :         op.ec_out,
     159            8607 :         op.cancelled.load(std::memory_order_acquire),
     160            8607 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
     161                 :         /*is_read=*/false, /*bytes=*/0, /*empty_buffer=*/false);
     162                 : 
     163            8607 :     coro_resume(&op);
     164            8607 : }
     165                 : 
     166                 : /** Construct and register a peer socket from an accepted fd.
     167                 : 
     168                 :     Creates a new socket impl via the acceptor's associated
     169                 :     socket service, registers it with the scheduler, and caches
     170                 :     the local and remote endpoints.
     171                 : 
     172                 :     @tparam SocketImpl The concrete socket implementation type.
     173                 :     @tparam AcceptorImpl The concrete acceptor implementation type.
     174                 :     @param acceptor_impl The acceptor that accepted the connection.
     175                 :     @param accepted_fd The accepted file descriptor (set to -1 on success).
     176                 :     @param peer_storage The peer address from accept().
     177                 :     @param impl_out Output pointer for the new socket impl.
     178                 :     @param ec_out Output pointer for any error.
     179                 :     @return True on success, false on failure.
     180                 : */
     181                 : template<typename SocketImpl, typename AcceptorImpl>
     182                 : bool
     183            8532 : setup_accepted_socket(
     184                 :     AcceptorImpl* acceptor_impl,
     185                 :     int& accepted_fd,
     186                 :     sockaddr_storage const& peer_storage,
     187                 :     socklen_t peer_addrlen,
     188                 :     io_object::implementation** impl_out,
     189                 :     std::error_code* ec_out)
     190                 : {
     191            8532 :     auto* socket_svc = acceptor_impl->service().stream_service();
     192            8532 :     if (!socket_svc)
     193                 :     {
     194 MIS           0 :         *ec_out = make_err(ENOENT);
     195               0 :         return false;
     196                 :     }
     197                 : 
     198 HIT        8532 :     auto& impl = static_cast<SocketImpl&>(*socket_svc->construct());
     199            8532 :     impl.set_socket(accepted_fd);
     200                 : 
     201            8532 :     impl.desc_state_.fd = accepted_fd;
     202                 :     {
     203            8532 :         std::lock_guard lock(impl.desc_state_.mutex);
     204            8532 :         impl.desc_state_.read_op    = nullptr;
     205            8532 :         impl.desc_state_.write_op   = nullptr;
     206            8532 :         impl.desc_state_.connect_op = nullptr;
     207            8532 :     }
     208            8532 :     socket_svc->scheduler().register_descriptor(accepted_fd, &impl.desc_state_);
     209                 : 
     210                 :     using ep_type = decltype(acceptor_impl->local_endpoint());
     211            8532 :     impl.set_endpoints(
     212                 :         acceptor_impl->local_endpoint(),
     213            8532 :         from_sockaddr_as(
     214                 :             peer_storage,
     215                 :             peer_addrlen,
     216                 :             ep_type{}));
     217                 : 
     218            8532 :     if (impl_out)
     219            8532 :         *impl_out = &impl;
     220            8532 :     accepted_fd = -1;
     221            8532 :     return true;
     222                 : }
     223                 : 
     224                 : /** Complete an accept operation.
     225                 : 
     226                 :     Sets up the peer socket on success, or closes the accepted
     227                 :     fd on failure. Then resumes the caller via symmetric transfer.
     228                 : 
     229                 :     @tparam SocketImpl The concrete socket implementation type.
     230                 :     @tparam Op The concrete accept operation type.
     231                 :     @param op The operation to complete.
     232                 : */
     233                 : template<typename SocketImpl, typename Op>
     234                 : void
     235            8576 : complete_accept_op(Op& op)
     236                 : {
     237            8576 :     op.stop_cb.reset();
     238            8576 :     if (auto* sched = op.acceptor_impl_->desc_state_.scheduler_)
     239            8572 :         sched->reset_inline_budget();
     240                 : 
     241            8576 :     bool success =
     242            8576 :         (op.errn == 0 && !op.cancelled.load(std::memory_order_acquire));
     243                 : 
     244           17148 :     decode_io_result(
     245                 :         op.ec_out,
     246            8576 :         op.cancelled.load(std::memory_order_acquire),
     247            8576 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
     248                 :         /*is_read=*/false, /*bytes=*/0, /*empty_buffer=*/false);
     249                 : 
     250            8576 :     if (success && op.accepted_fd >= 0 && op.acceptor_impl_)
     251                 :     {
     252            8532 :         if (!setup_accepted_socket<SocketImpl>(
     253            8532 :                 op.acceptor_impl_, op.accepted_fd, op.peer_storage,
     254                 :                 op.peer_addrlen, op.impl_out, op.ec_out))
     255 MIS           0 :             success = false;
     256                 :     }
     257                 : 
     258 HIT        8576 :     if (!success || !op.acceptor_impl_)
     259                 :     {
     260              44 :         if (op.accepted_fd >= 0)
     261                 :         {
     262               2 :             ::close(op.accepted_fd);
     263               2 :             op.accepted_fd = -1;
     264                 :         }
     265              44 :         if (op.impl_out)
     266              44 :             *op.impl_out = nullptr;
     267                 :     }
     268                 : 
     269            8576 :     coro_resume(&op);
     270            8576 : }
     271                 : 
     272                 : /** Complete a datagram operation (send_to or recv_from).
     273                 : 
     274                 :     For recv_from operations, writes the source endpoint from the
     275                 :     recorded sockaddr_storage into the caller's endpoint pointer.
     276                 :     Then resumes the caller via symmetric transfer.
     277                 : 
     278                 :     @tparam Op The concrete datagram operation type.
     279                 :     @param op The operation to complete.
     280                 : */
     281                 : template<typename Op>
     282                 : void
     283              58 : complete_datagram_op(Op& op)
     284                 : {
     285              58 :     op.stop_cb.reset();
     286              58 :     op.socket_impl_->desc_state_.scheduler_->reset_inline_budget();
     287                 : 
     288                 :     // No EOF: a zero-length datagram is valid (success with 0 bytes).
     289             116 :     decode_io_result(
     290                 :         op.ec_out,
     291              58 :         op.cancelled.load(std::memory_order_acquire),
     292              58 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
     293                 :         /*is_read=*/false, /*bytes=*/0, /*empty_buffer=*/false);
     294                 : 
     295              58 :     *op.bytes_out = op.bytes_transferred;
     296                 : 
     297              58 :     coro_resume(&op);
     298              58 : }
     299                 : 
     300                 : /** Complete a datagram operation with source endpoint capture.
     301                 : 
     302                 :     For recv_from operations, writes the source endpoint from the
     303                 :     recorded sockaddr_storage into the caller's endpoint pointer.
     304                 :     Then resumes the caller via symmetric transfer.
     305                 : 
     306                 :     @tparam Op The concrete datagram operation type.
     307                 :     @param op The operation to complete.
     308                 :     @param source_out Optional pointer to store source endpoint
     309                 :         (non-null for recv_from, null for send_to).
     310                 : */
     311                 : template<typename Op, typename Endpoint>
     312                 : void
     313              74 : complete_datagram_op(Op& op, Endpoint* source_out)
     314                 : {
     315              74 :     op.stop_cb.reset();
     316              74 :     op.socket_impl_->desc_state_.scheduler_->reset_inline_budget();
     317                 : 
     318                 :     // No EOF: a zero-length datagram is valid (success with 0 bytes).
     319             148 :     decode_io_result(
     320                 :         op.ec_out,
     321              74 :         op.cancelled.load(std::memory_order_acquire),
     322              74 :         op.errn != 0 ? make_err(op.errn) : std::error_code{},
     323                 :         /*is_read=*/false, /*bytes=*/0, /*empty_buffer=*/false);
     324                 : 
     325              74 :     *op.bytes_out = op.bytes_transferred;
     326                 : 
     327             124 :     if (source_out && !op.cancelled.load(std::memory_order_acquire) &&
     328              50 :         op.errn == 0)
     329             100 :         *source_out = from_sockaddr_as(
     330              50 :             op.source_storage,
     331                 :             op.source_addrlen,
     332                 :             Endpoint{});
     333                 : 
     334              74 :     coro_resume(&op);
     335              74 : }
     336                 : 
     337                 : } // namespace boost::corosio::detail
     338                 : 
     339                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_OP_COMPLETE_HPP
        

Generated by: LCOV version 2.3