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: 97.6 % 41 40 1
Test Date: 2026-11-13 14:41:09 Functions: 85.4 % 82 70 12

           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 family and local endpoint, and the
      31                 :     synchronous accessors (native_handle / options / bind). The impl
      32                 :     lifecycle (the intrusive refcount + the owning service's recycling
      33                 :     pool) is carried by `io_object::implementation`, not by this base.
      34                 : 
      35                 :     reactor_basic_socket derives from this and adds the readiness machinery
      36                 :     (descriptor_state, register_op, the cancel/close that deregister from
      37                 :     the reactor). io_uring's socket impls derive from it and add their op
      38                 :     slots + SQE submission. This is the socket-layer analogue of io_uring
      39                 :     deriving from reactor_scheduler: io_uring sockets share the reactor's
      40                 :     readiness-agnostic socket surface, while the on-EAGAIN action (park vs
      41                 :     submit-SQE) and the op model stay backend-specific.
      42                 : 
      43                 :     @tparam Derived   The concrete socket type (CRTP).
      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                 : {
      55                 : protected:
      56                 :     // CRTP base: not publicly constructible. The check's preferred fix
      57                 :     // (private ctor + `friend Derived`) is infeasible here — the reactor
      58                 :     // sockets reach this base through intermediate templates
      59                 :     // (reactor_stream_socket -> reactor_basic_socket) that are not `Derived`,
      60                 :     // so a private ctor would stop those intermediates from constructing it.
      61                 :     // Protected is the correct access; suppress the private-only suggestion.
      62                 :     // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
      63 HIT        5859 :     native_socket_base() = default;
      64                 : 
      65                 :     int fd_ = -1;
      66                 :     // Option calls, connects and datagram sends all need the family.
      67                 :     // Backends seed it where they know it and clear it whenever the
      68                 :     // descriptor goes; otherwise it is looked up once, on first use.
      69                 :     mutable int family_ = AF_UNSPEC;
      70                 :     // mutable so a derived const local_endpoint() override can lazily fill
      71                 :     // it via getsockname() on first read (io_uring's lazy_pending state).
      72                 :     mutable Endpoint local_endpoint_;
      73                 : 
      74                 : public:
      75            5859 :     ~native_socket_base() override = default;
      76                 : 
      77                 :     /// Return the underlying file descriptor.
      78           93314 :     native_handle_type native_handle() const noexcept override
      79                 :     {
      80           93314 :         return fd_;
      81                 :     }
      82                 : 
      83                 :     /// Return the socket's address family.
      84            7464 :     corosio::family family() const noexcept override
      85                 :     {
      86            7464 :         return to_family(native_family());
      87                 :     }
      88                 : 
      89                 :     /** Return the descriptor's native address family.
      90                 : 
      91                 :         @return `AF_INET`, `AF_INET6`, `AF_UNIX`, or `AF_UNSPEC` if the
      92                 :             socket is closed or the lookup fails.
      93                 :     */
      94           16389 :     int native_family() const noexcept
      95                 :     {
      96           16389 :         if (fd_ < 0)
      97 MIS           0 :             return AF_UNSPEC;
      98                 :         // A failed lookup stays uncached, so the next call retries.
      99 HIT       16389 :         if (family_ == AF_UNSPEC)
     100               2 :             family_ = socket_family(fd_);
     101           16389 :         return family_;
     102                 :     }
     103                 : 
     104                 :     /// Return the cached local endpoint.
     105             200 :     Endpoint local_endpoint() const noexcept override
     106                 :     {
     107             200 :         return local_endpoint_;
     108                 :     }
     109                 : 
     110                 :     /// Return true if the socket has an open file descriptor.
     111                 :     bool is_open() const noexcept
     112                 :     {
     113                 :         return fd_ >= 0;
     114                 :     }
     115                 : 
     116                 :     /// Set a socket option.
     117             117 :     std::error_code set_option(
     118                 :         int level,
     119                 :         int optname,
     120                 :         void const* data,
     121                 :         std::size_t size) noexcept override
     122                 :     {
     123             117 :         if (::setsockopt(
     124             117 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
     125               8 :             return make_err(errno);
     126             109 :         return {};
     127                 :     }
     128                 : 
     129                 :     /// Get a socket option.
     130                 :     std::error_code
     131             170 :     get_option(int level, int optname, void* data, std::size_t* size)
     132                 :         const noexcept override
     133                 :     {
     134             170 :         socklen_t len = static_cast<socklen_t>(*size);
     135             170 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     136              13 :             return make_err(errno);
     137             157 :         *size = static_cast<std::size_t>(len);
     138             157 :         return {};
     139                 :     }
     140                 : 
     141                 :     /// Assign the file descriptor and, when known, its family.
     142            6667 :     void set_socket(int fd, int family = AF_UNSPEC) noexcept
     143                 :     {
     144            6667 :         fd_     = fd;
     145            6667 :         family_ = family;
     146            6667 :     }
     147                 : 
     148                 :     /// Cache the local endpoint.
     149                 :     void set_local_endpoint(Endpoint ep) noexcept
     150                 :     {
     151                 :         local_endpoint_ = ep;
     152                 :     }
     153                 : 
     154                 :     /** Bind the socket to a local endpoint.
     155                 : 
     156                 :         Calls ::bind() and caches the resulting local endpoint via
     157                 :         getsockname(). Readiness-agnostic; usable by any fd backend.
     158                 : 
     159                 :         @param ep The endpoint to bind to.
     160                 :         @return Error code on failure, empty on success.
     161                 :     */
     162             278 :     std::error_code do_bind(Endpoint const& ep) noexcept
     163                 :     {
     164             278 :         sockaddr_storage storage{};
     165             278 :         socklen_t addrlen = to_sockaddr(ep, native_family(), storage);
     166             278 :         if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) != 0)
     167              20 :             return make_err(errno);
     168                 : 
     169             258 :         sockaddr_storage local_storage{};
     170             258 :         socklen_t local_len = sizeof(local_storage);
     171             258 :         if (::getsockname(
     172             258 :                 fd_, reinterpret_cast<sockaddr*>(&local_storage), &local_len) ==
     173                 :             0)
     174             197 :             local_endpoint_ =
     175             258 :                 from_sockaddr_as(local_storage, local_len, Endpoint{});
     176                 : 
     177             258 :         return {};
     178                 :     }
     179                 : };
     180                 : 
     181                 : } // namespace boost::corosio::detail
     182                 : 
     183                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP
        

Generated by: LCOV version 2.3