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 | ||||||