TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
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_REACTOR_REACTOR_ACCEPTOR_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
12 :
13 : #include <boost/corosio/tcp_acceptor.hpp>
14 : #include <boost/corosio/detail/intrusive.hpp>
15 : #include <boost/corosio/native/detail/reactor/reactor_op_base.hpp>
16 : #include <boost/corosio/native/detail/make_err.hpp>
17 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
18 :
19 : #include <memory>
20 : #include <mutex>
21 : #include <utility>
22 :
23 : #include <errno.h>
24 : #include <netinet/in.h>
25 : #include <sys/socket.h>
26 : #include <unistd.h>
27 :
28 : namespace boost::corosio::detail {
29 :
30 : /** CRTP base for reactor-backed acceptor implementations.
31 :
32 : Provides shared data members, trivial virtual overrides, and
33 : non-virtual helper methods for cancellation and close. Concrete
34 : backends inherit and add `cancel()`, `close_socket()`, and
35 : `accept()` overrides that delegate to the `do_*` helpers.
36 :
37 : @tparam Derived The concrete acceptor type (CRTP).
38 : @tparam Service The backend's acceptor service type.
39 : @tparam Op The backend's base op type.
40 : @tparam AcceptOp The backend's accept op type.
41 : @tparam DescState The backend's descriptor_state type.
42 : */
43 : template<
44 : class Derived,
45 : class Service,
46 : class Op,
47 : class AcceptOp,
48 : class DescState>
49 : class reactor_acceptor
50 : : public tcp_acceptor::implementation
51 : , public std::enable_shared_from_this<Derived>
52 : , public intrusive_list<Derived>::node
53 : {
54 : friend Derived;
55 :
56 HIT 141 : explicit reactor_acceptor(Service& svc) noexcept : svc_(svc) {}
57 :
58 : protected:
59 : Service& svc_;
60 : int fd_ = -1;
61 : endpoint local_endpoint_;
62 :
63 : public:
64 : /// Pending accept operation slot.
65 : AcceptOp acc_;
66 :
67 : /// Per-descriptor state for persistent reactor registration.
68 : DescState desc_state_;
69 :
70 141 : ~reactor_acceptor() override = default;
71 :
72 : /// Return the underlying file descriptor.
73 : int native_handle() const noexcept
74 : {
75 : return fd_;
76 : }
77 :
78 : /// Return the cached local endpoint.
79 7607 : endpoint local_endpoint() const noexcept override
80 : {
81 7607 : return local_endpoint_;
82 : }
83 :
84 : /// Return true if the acceptor has an open file descriptor.
85 8467 : bool is_open() const noexcept override
86 : {
87 8467 : return fd_ >= 0;
88 : }
89 :
90 : /// Set a socket option.
91 135 : std::error_code set_option(
92 : int level,
93 : int optname,
94 : void const* data,
95 : std::size_t size) noexcept override
96 : {
97 135 : if (::setsockopt(
98 135 : fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
99 MIS 0 : return make_err(errno);
100 HIT 135 : return {};
101 : }
102 :
103 : /// Get a socket option.
104 : std::error_code
105 MIS 0 : get_option(int level, int optname, void* data, std::size_t* size)
106 : const noexcept override
107 : {
108 0 : socklen_t len = static_cast<socklen_t>(*size);
109 0 : if (::getsockopt(fd_, level, optname, data, &len) != 0)
110 0 : return make_err(errno);
111 0 : *size = static_cast<std::size_t>(len);
112 0 : return {};
113 : }
114 :
115 : /// Cache the local endpoint.
116 HIT 132 : void set_local_endpoint(endpoint ep) noexcept
117 : {
118 132 : local_endpoint_ = ep;
119 132 : }
120 :
121 : /// Return a reference to the owning service.
122 7481 : Service& service() noexcept
123 : {
124 7481 : return svc_;
125 : }
126 :
127 : /** Cancel a single pending operation.
128 :
129 : Claims the operation from the read_op descriptor slot
130 : under the mutex and posts it to the scheduler as cancelled.
131 :
132 : @param op The operation to cancel.
133 : */
134 : void cancel_single_op(Op& op) noexcept;
135 :
136 : /** Cancel the pending accept operation.
137 :
138 : Invoked by the derived class's cancel() override.
139 : */
140 : void do_cancel() noexcept;
141 :
142 : /** Close the acceptor and cancel pending operations.
143 :
144 : Invoked by the derived class's close_socket(). The
145 : derived class may add backend-specific cleanup after
146 : calling this method.
147 : */
148 : void do_close_socket() noexcept;
149 :
150 : /** Bind the acceptor socket to an endpoint.
151 :
152 : Caches the resolved local endpoint (including ephemeral
153 : port) after a successful bind.
154 :
155 : @param ep The endpoint to bind to.
156 : @return The error code from bind(), or success.
157 : */
158 : std::error_code do_bind(endpoint ep);
159 :
160 : /** Start listening on the acceptor socket.
161 :
162 : Registers the file descriptor with the reactor after
163 : a successful listen() call.
164 :
165 : @param backlog The listen backlog.
166 : @return The error code from listen(), or success.
167 : */
168 : std::error_code do_listen(int backlog);
169 : };
170 :
171 : template<
172 : class Derived,
173 : class Service,
174 : class Op,
175 : class AcceptOp,
176 : class DescState>
177 : void
178 10 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::cancel_single_op(
179 : Op& op) noexcept
180 : {
181 10 : auto self = this->weak_from_this().lock();
182 10 : if (!self)
183 MIS 0 : return;
184 :
185 HIT 10 : op.request_cancel();
186 :
187 10 : reactor_op_base* claimed = nullptr;
188 : {
189 10 : std::lock_guard lock(desc_state_.mutex);
190 10 : if (desc_state_.read_op == &op)
191 8 : claimed = std::exchange(desc_state_.read_op, nullptr);
192 10 : }
193 10 : if (claimed)
194 : {
195 8 : op.impl_ptr = self;
196 8 : svc_.post(&op);
197 8 : svc_.work_finished();
198 : }
199 10 : }
200 :
201 : template<
202 : class Derived,
203 : class Service,
204 : class Op,
205 : class AcceptOp,
206 : class DescState>
207 : void
208 4 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::
209 : do_cancel() noexcept
210 : {
211 4 : cancel_single_op(acc_);
212 4 : }
213 :
214 : template<
215 : class Derived,
216 : class Service,
217 : class Op,
218 : class AcceptOp,
219 : class DescState>
220 : void
221 558 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::
222 : do_close_socket() noexcept
223 : {
224 558 : auto self = this->weak_from_this().lock();
225 558 : if (self)
226 : {
227 558 : acc_.request_cancel();
228 :
229 558 : reactor_op_base* claimed = nullptr;
230 : {
231 558 : std::lock_guard lock(desc_state_.mutex);
232 558 : claimed = std::exchange(desc_state_.read_op, nullptr);
233 558 : desc_state_.read_ready = false;
234 558 : desc_state_.write_ready = false;
235 :
236 558 : if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
237 MIS 0 : desc_state_.impl_ref_ = self;
238 HIT 558 : }
239 :
240 558 : if (claimed)
241 : {
242 4 : acc_.impl_ptr = self;
243 4 : svc_.post(&acc_);
244 4 : svc_.work_finished();
245 : }
246 : }
247 :
248 558 : if (fd_ >= 0)
249 : {
250 138 : if (desc_state_.registered_events != 0)
251 132 : svc_.scheduler().deregister_descriptor(fd_);
252 138 : ::close(fd_);
253 138 : fd_ = -1;
254 : }
255 :
256 558 : desc_state_.fd = -1;
257 558 : desc_state_.registered_events = 0;
258 :
259 558 : local_endpoint_ = endpoint{};
260 558 : }
261 :
262 : template<
263 : class Derived,
264 : class Service,
265 : class Op,
266 : class AcceptOp,
267 : class DescState>
268 : std::error_code
269 136 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::do_bind(
270 : endpoint ep)
271 : {
272 136 : sockaddr_storage storage{};
273 136 : socklen_t addrlen = to_sockaddr(ep, storage);
274 136 : if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) < 0)
275 4 : return make_err(errno);
276 :
277 : // Cache local endpoint (resolves ephemeral port)
278 132 : sockaddr_storage local{};
279 132 : socklen_t local_len = sizeof(local);
280 132 : if (::getsockname(fd_, reinterpret_cast<sockaddr*>(&local), &local_len) ==
281 : 0)
282 132 : set_local_endpoint(from_sockaddr(local));
283 :
284 132 : return {};
285 : }
286 :
287 : template<
288 : class Derived,
289 : class Service,
290 : class Op,
291 : class AcceptOp,
292 : class DescState>
293 : std::error_code
294 132 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::do_listen(
295 : int backlog)
296 : {
297 132 : if (::listen(fd_, backlog) < 0)
298 MIS 0 : return make_err(errno);
299 :
300 HIT 132 : svc_.scheduler().register_descriptor(fd_, &desc_state_);
301 132 : return {};
302 : }
303 :
304 : } // namespace boost::corosio::detail
305 :
306 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
|