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