100.00% Lines (34/34) 100.00% Functions (9/9)
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, and the synchronous
31 - bases (enable_shared_from_this + the service's intrusive tracking node), 31 + accessors (native_handle / options / bind). The impl lifecycle (the
32 - and the synchronous accessors (native_handle / options / bind). 32 + intrusive refcount + the owning service's recycling pool) is carried
  33 + by `io_object::implementation`, not by this base.
33   34  
34   reactor_basic_socket derives from this and adds the readiness machinery 35   reactor_basic_socket derives from this and adds the readiness machinery
35   (descriptor_state, register_op, the cancel/close that deregister from 36   (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   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   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   deriving from reactor_scheduler: io_uring sockets share the reactor's
39   readiness-agnostic socket surface, while the on-EAGAIN action (park vs 40   readiness-agnostic socket surface, while the on-EAGAIN action (park vs
40   submit-SQE) and the op model stay backend-specific. 41   submit-SQE) and the op model stay backend-specific.
41   42  
42 - @tparam Derived The concrete socket type (CRTP, for shared_from_this 43 + @tparam Derived The concrete socket type (CRTP).
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 std::enable_shared_from_this<Derived>  
54   : public ImplBase 53   : public ImplBase
55   { 54   {
56   protected: 55   protected:
57   // CRTP base: not publicly constructible. The check's preferred fix 56   // CRTP base: not publicly constructible. The check's preferred fix
58   // (private ctor + `friend Derived`) is infeasible here — the reactor 57   // (private ctor + `friend Derived`) is infeasible here — the reactor
59   // sockets reach this base through intermediate templates 58   // sockets reach this base through intermediate templates
60   // (reactor_stream_socket -> reactor_basic_socket) that are not `Derived`, 59   // (reactor_stream_socket -> reactor_basic_socket) that are not `Derived`,
61   // so a private ctor would stop those intermediates from constructing it. 60   // so a private ctor would stop those intermediates from constructing it.
62   // Protected is the correct access; suppress the private-only suggestion. 61   // Protected is the correct access; suppress the private-only suggestion.
63   // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) 62   // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
HITCBC 64   15068 native_socket_base() = default; 63   2765 native_socket_base() = default;
65   64  
66   int fd_ = -1; 65   int fd_ = -1;
67   // mutable so a derived const local_endpoint() override can lazily fill 66   // mutable so a derived const local_endpoint() override can lazily fill
68   // it via getsockname() on first read (io_uring's lazy_pending state). 67   // it via getsockname() on first read (io_uring's lazy_pending state).
69   mutable Endpoint local_endpoint_; 68   mutable Endpoint local_endpoint_;
70   69  
71   public: 70   public:
HITCBC 72   15068 ~native_socket_base() override = default; 71   2765 ~native_socket_base() override = default;
73   72  
74   /// Return the underlying file descriptor. 73   /// Return the underlying file descriptor.
HITCBC 75   46998 native_handle_type native_handle() const noexcept override 74   49653 native_handle_type native_handle() const noexcept override
76   { 75   {
HITCBC 77   46998 return fd_; 76   49653 return fd_;
78   } 77   }
79   78  
HITCBC 80   585 corosio::family family() const noexcept override 79   593 corosio::family family() const noexcept override
81   { 80   {
HITCBC 82   585 return to_family(socket_family(fd_)); 81   593 return to_family(socket_family(fd_));
83   } 82   }
84   83  
85   /// Return the cached local endpoint. 84   /// Return the cached local endpoint.
HITCBC 86   194 Endpoint local_endpoint() const noexcept override 85   196 Endpoint local_endpoint() const noexcept override
87   { 86   {
HITCBC 88   194 return local_endpoint_; 87   196 return local_endpoint_;
89   } 88   }
90   89  
91   /// Return true if the socket has an open file descriptor. 90   /// Return true if the socket has an open file descriptor.
92   bool is_open() const noexcept 91   bool is_open() const noexcept
93   { 92   {
94   return fd_ >= 0; 93   return fd_ >= 0;
95   } 94   }
96   95  
97   /// Set a socket option. 96   /// Set a socket option.
HITCBC 98   117 std::error_code set_option( 97   117 std::error_code set_option(
99   int level, 98   int level,
100   int optname, 99   int optname,
101   void const* data, 100   void const* data,
102   std::size_t size) noexcept override 101   std::size_t size) noexcept override
103   { 102   {
HITCBC 104   117 if (::setsockopt( 103   117 if (::setsockopt(
HITCBC 105   117 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0) 104   117 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
HITCBC 106   8 return make_err(errno); 105   8 return make_err(errno);
HITCBC 107   109 return {}; 106   109 return {};
108   } 107   }
109   108  
110   /// Get a socket option. 109   /// Get a socket option.
111   std::error_code 110   std::error_code
HITCBC 112   170 get_option(int level, int optname, void* data, std::size_t* size) 111   170 get_option(int level, int optname, void* data, std::size_t* size)
113   const noexcept override 112   const noexcept override
114   { 113   {
HITCBC 115   170 socklen_t len = static_cast<socklen_t>(*size); 114   170 socklen_t len = static_cast<socklen_t>(*size);
HITCBC 116   170 if (::getsockopt(fd_, level, optname, data, &len) != 0) 115   170 if (::getsockopt(fd_, level, optname, data, &len) != 0)
HITCBC 117   13 return make_err(errno); 116   13 return make_err(errno);
HITCBC 118   157 *size = static_cast<std::size_t>(len); 117   157 *size = static_cast<std::size_t>(len);
HITCBC 119   157 return {}; 118   157 return {};
120   } 119   }
121   120  
122   /// Assign the file descriptor. 121   /// Assign the file descriptor.
HITCBC 123   4611 void set_socket(int fd) noexcept 122   4868 void set_socket(int fd) noexcept
124   { 123   {
HITCBC 125   4611 fd_ = fd; 124   4868 fd_ = fd;
HITCBC 126   4611 } 125   4868 }
127   126  
128   /// Cache the local endpoint. 127   /// Cache the local endpoint.
129   void set_local_endpoint(Endpoint ep) noexcept 128   void set_local_endpoint(Endpoint ep) noexcept
130   { 129   {
131   local_endpoint_ = ep; 130   local_endpoint_ = ep;
132   } 131   }
133   132  
134   /** Bind the socket to a local endpoint. 133   /** Bind the socket to a local endpoint.
135   134  
136   Calls ::bind() and caches the resulting local endpoint via 135   Calls ::bind() and caches the resulting local endpoint via
137   getsockname(). Readiness-agnostic; usable by any fd backend. 136   getsockname(). Readiness-agnostic; usable by any fd backend.
138   137  
139   @param ep The endpoint to bind to. 138   @param ep The endpoint to bind to.
140   @return Error code on failure, empty on success. 139   @return Error code on failure, empty on success.
141   */ 140   */
HITCBC 142   270 std::error_code do_bind(Endpoint const& ep) noexcept 141   274 std::error_code do_bind(Endpoint const& ep) noexcept
143   { 142   {
HITCBC 144   270 sockaddr_storage storage{}; 143   274 sockaddr_storage storage{};
HITCBC 145   270 socklen_t addrlen = to_sockaddr(ep, socket_family(fd_), storage); 144   274 socklen_t addrlen = to_sockaddr(ep, socket_family(fd_), storage);
HITCBC 146   270 if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) != 0) 145   274 if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) != 0)
HITCBC 147   20 return make_err(errno); 146   20 return make_err(errno);
148   147  
HITCBC 149   250 sockaddr_storage local_storage{}; 148   254 sockaddr_storage local_storage{};
HITCBC 150   250 socklen_t local_len = sizeof(local_storage); 149   254 socklen_t local_len = sizeof(local_storage);
HITCBC 151   250 if (::getsockname( 150   254 if (::getsockname(
HITCBC 152   250 fd_, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 151   254 fd_, reinterpret_cast<sockaddr*>(&local_storage), &local_len) ==
153   0) 152   0)
HITCBC 154   189 local_endpoint_ = 153   193 local_endpoint_ =
HITCBC 155   250 from_sockaddr_as(local_storage, local_len, Endpoint{}); 154   254 from_sockaddr_as(local_storage, local_len, Endpoint{});
156   155  
HITCBC 157   250 return {}; 156   254 return {};
158   } 157   }
159   }; 158   };
160   159  
161   } // namespace boost::corosio::detail 160   } // namespace boost::corosio::detail
162   161  
163   #endif // BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP 162   #endif // BOOST_COROSIO_NATIVE_DETAIL_NATIVE_SOCKET_BASE_HPP