TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_BASIC_SOCKET_HPP
12 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_BASIC_SOCKET_HPP
13 :
14 : #include <boost/corosio/detail/config.hpp>
15 : #include <boost/corosio/detail/intrusive.hpp>
16 : #include <boost/corosio/detail/native_handle.hpp>
17 : #include <boost/corosio/endpoint.hpp>
18 : #include <boost/corosio/native/detail/native_socket_base.hpp>
19 : #include <boost/corosio/native/detail/reactor/reactor_io_core.hpp>
20 : #include <boost/corosio/native/detail/make_err.hpp>
21 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
22 :
23 : #include <cstring>
24 : #include <memory>
25 : #include <mutex>
26 : #include <utility>
27 :
28 : #include <errno.h>
29 : #include <netinet/in.h>
30 : #include <sys/socket.h>
31 : #include <unistd.h>
32 :
33 : namespace boost::corosio::detail {
34 :
35 : /** CRTP base for reactor-backed socket implementations.
36 :
37 : Extracts the shared data members and virtual overrides that are
38 : identical across TCP (reactor_stream_socket) and UDP
39 : (reactor_datagram_socket). The register/park/cancel/teardown
40 : protocol lives in reactor_io_core.
41 :
42 : Derived classes provide CRTP callbacks that enumerate their
43 : specific op slots so cancel/close can iterate them generically.
44 :
45 : @tparam Derived The concrete socket type (CRTP).
46 : @tparam ImplBase The public vtable base (tcp_socket::implementation
47 : or udp_socket::implementation).
48 : @tparam Service The backend's service type.
49 : @tparam DescState The backend's descriptor_state type.
50 : @tparam Endpoint The endpoint type (endpoint or local_endpoint).
51 : */
52 : template<
53 : class Derived,
54 : class ImplBase,
55 : class Service,
56 : class DescState,
57 : class Endpoint = endpoint>
58 : class reactor_basic_socket
59 : : public native_socket_base<Derived, ImplBase, Endpoint>
60 : , public reactor_io_core<Derived, Service, DescState>
61 : , public intrusive_list<Derived>::node
62 : {
63 : friend Derived;
64 :
65 : template<class, class, class, class, class, class, class, class, class>
66 : friend class reactor_stream_socket;
67 :
68 : template<
69 : class,
70 : class,
71 : class,
72 : class,
73 : class,
74 : class,
75 : class,
76 : class,
77 : class,
78 : class,
79 : class>
80 : friend class reactor_datagram_socket;
81 :
82 : using core_type = reactor_io_core<Derived, Service, DescState>;
83 :
84 HIT 5656 : explicit reactor_basic_socket(Service& svc) noexcept : core_type(svc) {}
85 :
86 : protected:
87 : // fd_ / local_endpoint_ and the synchronous accessors (native_handle,
88 : // is_open, set_option/get_option, set_socket/set_local_endpoint, do_bind)
89 : // live in native_socket_base — the readiness/completion-agnostic base
90 : // shared with io_uring's sockets. The using-declarations make the
91 : // inherited members visible to this template's own unqualified
92 : // references below (two-phase lookup).
93 : using native_socket_base<Derived, ImplBase, Endpoint>::fd_;
94 : using native_socket_base<Derived, ImplBase, Endpoint>::local_endpoint_;
95 :
96 : public:
97 5656 : ~reactor_basic_socket() override = default;
98 :
99 : /** Assign the fd, initialize descriptor state, and register with
100 : the reactor.
101 :
102 : @param fd The descriptor to adopt.
103 :
104 : @return The error if the reactor rejects the descriptor, in
105 : which case the implementation is left closed and the caller
106 : retains ownership of @a fd; otherwise a default constructed
107 : error code.
108 : */
109 14051 : std::error_code init_and_register(int fd) noexcept
110 : {
111 14051 : fd_ = fd;
112 14051 : if (auto ec = this->register_fd(fd))
113 : {
114 3 : fd_ = -1;
115 3 : return ec;
116 : }
117 14048 : return {};
118 : }
119 :
120 : /// Cancel all pending operations.
121 293 : void do_cancel() noexcept
122 : {
123 293 : this->cancel_all();
124 293 : }
125 :
126 : /** Close the socket and cancel pending operations.
127 :
128 : Invoked by the derived class's close_socket(). The
129 : derived class may add backend-specific cleanup after
130 : calling this method.
131 : */
132 84002 : void do_close_socket() noexcept
133 : {
134 84002 : this->abandon_all();
135 84002 : if (fd_ >= 0)
136 : {
137 20541 : ::close(fd_);
138 20541 : fd_ = -1;
139 : }
140 84002 : local_endpoint_ = Endpoint{};
141 84002 : }
142 :
143 : /** Release the socket without closing the fd.
144 :
145 : Like do_close_socket() but does not call ::close().
146 : Returns the fd so the caller can take ownership.
147 : */
148 20 : native_handle_type do_release_socket() noexcept
149 : {
150 20 : this->abandon_all();
151 20 : native_handle_type released = fd_;
152 20 : fd_ = -1;
153 20 : local_endpoint_ = Endpoint{};
154 20 : return released;
155 : }
156 :
157 : /** Reset descriptor state for recycling.
158 :
159 : Called by the owning service's `construct()` when popping this
160 : impl back off the free list. Actively re-initializes every
161 : field it owns rather than trusting `close_socket()`'s prior
162 : writes to still be there — `poison()` below depends on that,
163 : and a future field added to either side without the other
164 : would otherwise go unnoticed until this one re-sets it.
165 : `object_ref_` and `is_enqueued_` are asserted instead: `poison()`
166 : never touches them, so they must already hold their
167 : zero-action-complete state.
168 :
169 : @pre refs_ == 0, fd closed and deregistered, no op in flight.
170 : */
171 21191 : void reuse() noexcept
172 : {
173 21191 : fd_ = -1;
174 21191 : local_endpoint_ = Endpoint{};
175 21191 : this->reset_desc_state();
176 21191 : }
177 :
178 : #if !defined(NDEBUG)
179 : /** Poison the fields `reuse()` re-initializes, before the pool
180 : parks this impl on the free list.
181 :
182 : A field `reuse()` forgets to reset then reads back as this
183 : unmistakable byte pattern instead of silently matching
184 : whatever `close_socket()` happened to leave, turning a missed
185 : reset into an immediate, loud failure rather than a latent
186 : stale-state bug. See `reactor_io_core::poison_desc_state()`
187 : for what is left alone.
188 :
189 : @pre refs_ == 0, fd closed and deregistered, no op in flight.
190 : */
191 26847 : void poison() noexcept
192 : {
193 53694 : auto smash = [](auto& field) {
194 53694 : std::memset(static_cast<void*>(&field), 0xDB, sizeof(field));
195 : };
196 26847 : smash(fd_);
197 26847 : smash(local_endpoint_);
198 26847 : this->poison_desc_state();
199 26847 : }
200 : #endif
201 : };
202 :
203 : } // namespace boost::corosio::detail
204 :
205 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_BASIC_SOCKET_HPP
|