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