96.88% Lines (31/32) 100.00% Functions (8/8)
TLA Baseline Branch
Line Hits Code Line Hits 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.
HITGNC   63 + 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:
HITGNC   71 + 21888 ~native_socket_base() override = default;
  72 +
  73 + /// Return the underlying file descriptor.
HITGNC   74 + 66551 native_handle_type native_handle() const noexcept override
  75 + {
HITGNC   76 + 66551 return fd_;
  77 + }
  78 +
  79 + /// Return the cached local endpoint.
HITGNC   80 + 106 Endpoint local_endpoint() const noexcept override
  81 + {
HITGNC   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.
HITGNC   92 + 66 std::error_code set_option(
  93 + int level, int optname, void const* data, std::size_t size)
  94 + noexcept override
  95 + {
HITGNC   96 + 66 if (::setsockopt(
HITGNC   97 + 66 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
HITGNC   98 + 2 return make_err(errno);
HITGNC   99 + 64 return {};
  100 + }
  101 +
  102 + /// Get a socket option.
HITGNC   103 + 102 std::error_code get_option(
  104 + int level, int optname, void* data, std::size_t* size)
  105 + const noexcept override
  106 + {
HITGNC   107 + 102 socklen_t len = static_cast<socklen_t>(*size);
HITGNC   108 + 102 if (::getsockopt(fd_, level, optname, data, &len) != 0)
MISUNC   109 + return make_err(errno);
HITGNC   110 + 102 *size = static_cast<std::size_t>(len);
HITGNC   111 + 102 return {};
  112 + }
  113 +
  114 + /// Assign the file descriptor.
HITGNC   115 + 7113 void set_socket(int fd) noexcept
  116 + {
HITGNC   117 + 7113 fd_ = fd;
HITGNC   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 + */
HITGNC   134 + 136 std::error_code do_bind(Endpoint const& ep) noexcept
  135 + {
HITGNC   136 + 136 sockaddr_storage storage{};
HITGNC   137 + 136 socklen_t addrlen = to_sockaddr(ep, socket_family(fd_), storage);
HITGNC   138 + 136 if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) != 0)
HITGNC   139 + 10 return make_err(errno);
  140 +
HITGNC   141 + 126 sockaddr_storage local_storage{};
HITGNC   142 + 126 socklen_t local_len = sizeof(local_storage);
HITGNC   143 + 126 if (::getsockname(
  144 + fd_, reinterpret_cast<sockaddr*>(&local_storage), &local_len)
HITGNC   145 + 126 == 0)
HITGNC   146 + 96 local_endpoint_ =
HITGNC   147 + 126 from_sockaddr_as(local_storage, local_len, Endpoint{});
  148 +
HITGNC   149 + 126 return {};
  150 + }
  151 + };
  152 +
  153 + } // namespace boost::corosio::detail
  154 +
  155 + #endif // BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP