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: 100.0 % 32 32
Test Date: 2026-09-09 02:31:18 Functions: 81.8 % 66 54 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 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                 :     // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
      64 HIT       14746 :     native_socket_base() = default;
      65                 : 
      66                 :     int fd_ = -1;
      67                 :     // mutable so a derived const local_endpoint() override can lazily fill
      68                 :     // it via getsockname() on first read (io_uring's lazy_pending state).
      69                 :     mutable Endpoint local_endpoint_;
      70                 : 
      71                 : public:
      72           14746 :     ~native_socket_base() override = default;
      73                 : 
      74                 :     /// Return the underlying file descriptor.
      75           46045 :     native_handle_type native_handle() const noexcept override
      76                 :     {
      77           46045 :         return fd_;
      78                 :     }
      79                 : 
      80                 :     /// Return the cached local endpoint.
      81             186 :     Endpoint local_endpoint() const noexcept override
      82                 :     {
      83             186 :         return local_endpoint_;
      84                 :     }
      85                 : 
      86                 :     /// Return true if the socket has an open file descriptor.
      87                 :     bool is_open() const noexcept
      88                 :     {
      89                 :         return fd_ >= 0;
      90                 :     }
      91                 : 
      92                 :     /// Set a socket option.
      93             111 :     std::error_code set_option(
      94                 :         int level,
      95                 :         int optname,
      96                 :         void const* data,
      97                 :         std::size_t size) noexcept override
      98                 :     {
      99             111 :         if (::setsockopt(
     100             111 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
     101               8 :             return make_err(errno);
     102             103 :         return {};
     103                 :     }
     104                 : 
     105                 :     /// Get a socket option.
     106                 :     std::error_code
     107             164 :     get_option(int level, int optname, void* data, std::size_t* size)
     108                 :         const noexcept override
     109                 :     {
     110             164 :         socklen_t len = static_cast<socklen_t>(*size);
     111             164 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     112              13 :             return make_err(errno);
     113             151 :         *size = static_cast<std::size_t>(len);
     114             151 :         return {};
     115                 :     }
     116                 : 
     117                 :     /// Assign the file descriptor.
     118            4515 :     void set_socket(int fd) noexcept
     119                 :     {
     120            4515 :         fd_ = fd;
     121            4515 :     }
     122                 : 
     123                 :     /// Cache the local endpoint.
     124                 :     void set_local_endpoint(Endpoint ep) noexcept
     125                 :     {
     126                 :         local_endpoint_ = ep;
     127                 :     }
     128                 : 
     129                 :     /** Bind the socket to a local endpoint.
     130                 : 
     131                 :         Calls ::bind() and caches the resulting local endpoint via
     132                 :         getsockname(). Readiness-agnostic; usable by any fd backend.
     133                 : 
     134                 :         @param ep The endpoint to bind to.
     135                 :         @return Error code on failure, empty on success.
     136                 :     */
     137             264 :     std::error_code do_bind(Endpoint const& ep) noexcept
     138                 :     {
     139             264 :         sockaddr_storage storage{};
     140             264 :         socklen_t addrlen = to_sockaddr(ep, socket_family(fd_), storage);
     141             264 :         if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) != 0)
     142              20 :             return make_err(errno);
     143                 : 
     144             244 :         sockaddr_storage local_storage{};
     145             244 :         socklen_t local_len = sizeof(local_storage);
     146             244 :         if (::getsockname(
     147             244 :                 fd_, reinterpret_cast<sockaddr*>(&local_storage), &local_len) ==
     148                 :             0)
     149             183 :             local_endpoint_ =
     150             244 :                 from_sockaddr_as(local_storage, local_len, Endpoint{});
     151                 : 
     152             244 :         return {};
     153                 :     }
     154                 : };
     155                 : 
     156                 : } // namespace boost::corosio::detail
     157                 : 
     158                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP
        

Generated by: LCOV version 2.3