LCOV - code coverage report
Current view: top level - corosio/native/detail - native_socket_base.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 96.9 % 32 31 1
Test Date: 2026-06-09 16:09:19 Functions: 72.7 % 66 48 18

           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_NATIVE_SOCKET_BASE_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/native_handle.hpp>
      14                 : #include <boost/corosio/endpoint.hpp>
      15                 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
      16                 : #include <boost/corosio/native/detail/make_err.hpp>
      17                 : 
      18                 : #include <memory>
      19                 : #include <system_error>
      20                 : 
      21                 : #include <errno.h>
      22                 : #include <sys/socket.h>
      23                 : 
      24                 : /*
      25                 :     Readiness/completion-agnostic socket base for the POSIX-fd backends.
      26                 : 
      27                 :     Holds the part of a socket impl that does not care whether the backend
      28                 :     is readiness-based (epoll/kqueue/select, which park ops on a
      29                 :     descriptor_state) or completion-based (io_uring, which submits SQEs):
      30                 :     the file descriptor, the cached local endpoint, the impl lifecycle
      31                 :     bases (enable_shared_from_this + the service's intrusive tracking node),
      32                 :     and the synchronous accessors (native_handle / options / bind).
      33                 : 
      34                 :     reactor_basic_socket derives from this and adds the readiness machinery
      35                 :     (descriptor_state, register_op, the cancel/close that deregister from
      36                 :     the reactor). io_uring's socket impls derive from it and add their op
      37                 :     slots + SQE submission. This is the socket-layer analogue of io_uring
      38                 :     deriving from reactor_scheduler: io_uring sockets share the reactor's
      39                 :     readiness-agnostic socket surface, while the on-EAGAIN action (park vs
      40                 :     submit-SQE) and the op model stay backend-specific.
      41                 : 
      42                 :     @tparam Derived   The concrete socket type (CRTP, for shared_from_this
      43                 :                       and the intrusive node).
      44                 :     @tparam ImplBase  The public vtable base (tcp_socket::implementation,
      45                 :                       udp_socket::implementation, ...).
      46                 :     @tparam Endpoint  The endpoint type (endpoint or local_endpoint).
      47                 : */
      48                 : 
      49                 : namespace boost::corosio::detail {
      50                 : 
      51                 : template<class Derived, class ImplBase, class Endpoint = endpoint>
      52                 : class native_socket_base
      53                 :     : public ImplBase
      54                 :     , public std::enable_shared_from_this<Derived>
      55                 : {
      56                 : protected:
      57                 :     // CRTP base: not publicly constructible. The check's preferred fix
      58                 :     // (private ctor + `friend Derived`) is infeasible here — the reactor
      59                 :     // sockets reach this base through intermediate templates
      60                 :     // (reactor_stream_socket -> reactor_basic_socket) that are not `Derived`,
      61                 :     // so a private ctor would stop those intermediates from constructing it.
      62                 :     // Protected is the correct access; suppress the private-only suggestion.
      63 HIT       21888 :     native_socket_base() = default;  // NOLINT(bugprone-crtp-constructor-accessibility)
      64                 : 
      65                 :     int      fd_ = -1;
      66                 :     // mutable so a derived const local_endpoint() override can lazily fill
      67                 :     // it via getsockname() on first read (io_uring's lazy_pending state).
      68                 :     mutable Endpoint local_endpoint_;
      69                 : 
      70                 : public:
      71           21888 :     ~native_socket_base() override = default;
      72                 : 
      73                 :     /// Return the underlying file descriptor.
      74           66551 :     native_handle_type native_handle() const noexcept override
      75                 :     {
      76           66551 :         return fd_;
      77                 :     }
      78                 : 
      79                 :     /// Return the cached local endpoint.
      80             106 :     Endpoint local_endpoint() const noexcept override
      81                 :     {
      82             106 :         return local_endpoint_;
      83                 :     }
      84                 : 
      85                 :     /// Return true if the socket has an open file descriptor.
      86                 :     bool is_open() const noexcept
      87                 :     {
      88                 :         return fd_ >= 0;
      89                 :     }
      90                 : 
      91                 :     /// Set a socket option.
      92              66 :     std::error_code set_option(
      93                 :         int level, int optname, void const* data, std::size_t size)
      94                 :         noexcept override
      95                 :     {
      96              66 :         if (::setsockopt(
      97              66 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
      98               2 :             return make_err(errno);
      99              64 :         return {};
     100                 :     }
     101                 : 
     102                 :     /// Get a socket option.
     103             102 :     std::error_code get_option(
     104                 :         int level, int optname, void* data, std::size_t* size)
     105                 :         const noexcept override
     106                 :     {
     107             102 :         socklen_t len = static_cast<socklen_t>(*size);
     108             102 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     109 MIS           0 :             return make_err(errno);
     110 HIT         102 :         *size = static_cast<std::size_t>(len);
     111             102 :         return {};
     112                 :     }
     113                 : 
     114                 :     /// Assign the file descriptor.
     115            7113 :     void set_socket(int fd) noexcept
     116                 :     {
     117            7113 :         fd_ = fd;
     118            7113 :     }
     119                 : 
     120                 :     /// Cache the local endpoint.
     121                 :     void set_local_endpoint(Endpoint ep) noexcept
     122                 :     {
     123                 :         local_endpoint_ = ep;
     124                 :     }
     125                 : 
     126                 :     /** Bind the socket to a local endpoint.
     127                 : 
     128                 :         Calls ::bind() and caches the resulting local endpoint via
     129                 :         getsockname(). Readiness-agnostic; usable by any fd backend.
     130                 : 
     131                 :         @param ep The endpoint to bind to.
     132                 :         @return Error code on failure, empty on success.
     133                 :     */
     134             136 :     std::error_code do_bind(Endpoint const& ep) noexcept
     135                 :     {
     136             136 :         sockaddr_storage storage{};
     137             136 :         socklen_t addrlen = to_sockaddr(ep, socket_family(fd_), storage);
     138             136 :         if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) != 0)
     139              10 :             return make_err(errno);
     140                 : 
     141             126 :         sockaddr_storage local_storage{};
     142             126 :         socklen_t        local_len = sizeof(local_storage);
     143             126 :         if (::getsockname(
     144                 :                 fd_, reinterpret_cast<sockaddr*>(&local_storage), &local_len)
     145             126 :             == 0)
     146              96 :             local_endpoint_ =
     147             126 :                 from_sockaddr_as(local_storage, local_len, Endpoint{});
     148                 : 
     149             126 :         return {};
     150                 :     }
     151                 : };
     152                 : 
     153                 : } // namespace boost::corosio::detail
     154                 : 
     155                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP
        

Generated by: LCOV version 2.3