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_REACTOR_REACTOR_SERVICE_FINALS_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
12 :
13 : /* Parameterized service implementation bases for reactor backends.
14 :
15 : One template per protocol (TCP, local stream, UDP, local datagram,
16 : acceptor). Named per-backend classes (e.g. epoll_tcp_service) inherit
17 : from these as final. The Derived parameter (CRTP) flows through to
18 : reactor_socket_service so construct() creates the correct named type.
19 : */
20 :
21 : #include <boost/corosio/native/detail/reactor/reactor_socket_finals.hpp>
22 : #include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
23 : #include <boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp>
24 : #include <boost/corosio/detail/tcp_service.hpp>
25 : #include <boost/corosio/detail/tcp_acceptor_service.hpp>
26 : #include <boost/corosio/detail/udp_service.hpp>
27 : #include <boost/corosio/detail/local_stream_service.hpp>
28 : #include <boost/corosio/detail/local_stream_acceptor_service.hpp>
29 : #include <boost/corosio/detail/local_datagram_service.hpp>
30 :
31 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
32 : #include <boost/corosio/native/detail/make_err.hpp>
33 : #include <boost/corosio/native/detail/validate_fd.hpp>
34 :
35 : #include <system_error>
36 : #include <type_traits>
37 :
38 : #include <sys/socket.h>
39 : #include <unistd.h>
40 :
41 : namespace boost::corosio::detail {
42 :
43 : // ============================================================
44 : // Shared socket creation helpers
45 : // ============================================================
46 :
47 : template<class Traits, class SocketFinal>
48 : std::error_code
49 HIT 5078 : do_open_socket(
50 : SocketFinal* socket_impl,
51 : int family,
52 : int type,
53 : int protocol,
54 : bool is_ip) noexcept
55 : {
56 5078 : socket_impl->close_socket();
57 :
58 5078 : int fd = Traits::create_socket(family, type, protocol);
59 5078 : if (fd < 0)
60 6 : return make_err(errno);
61 :
62 5072 : std::error_code ec = is_ip ? Traits::configure_ip_socket(fd, family)
63 142 : : Traits::configure_local_socket(fd);
64 :
65 5072 : if (ec)
66 : {
67 4 : ::close(fd);
68 4 : return ec;
69 : }
70 :
71 5068 : if (auto ec = socket_impl->init_and_register(fd))
72 : {
73 2 : ::close(fd);
74 2 : return ec;
75 : }
76 5066 : return {};
77 : }
78 :
79 : template<class Traits, class SocketFinal>
80 : std::error_code
81 368 : do_assign_fd(
82 : SocketFinal* socket_impl, int fd, int expected_type, bool is_ip) noexcept
83 : {
84 : // fd >= 0 guard: an unset socket_impl reports native_handle() == -1,
85 : // and a caller-supplied -1 must fail as a bad fd, not a self-assign.
86 368 : if (fd >= 0 && fd == socket_impl->native_handle())
87 10 : return std::make_error_code(std::errc::invalid_argument);
88 :
89 : // Validate before touching the held socket: a failed assign must
90 : // leave the object unchanged and the caller owning the fd.
91 358 : if (auto ec = validate_socket_fd(fd, expected_type, is_ip))
92 54 : return ec;
93 :
94 : // Adopt-only: do not mutate the caller's fd flags. Callers
95 : // pass fds they have already configured (e.g., from socketpair
96 : // or SCM_RIGHTS). Only non-mutating validation is performed.
97 304 : if (auto ec = Traits::validate_assigned_fd(fd))
98 1 : return ec;
99 :
100 303 : socket_impl->close_socket();
101 :
102 303 : if (auto ec = socket_impl->init_and_register(fd))
103 1 : return ec;
104 :
105 : // Best-effort: refresh endpoint caches.
106 : using endpoint_type =
107 : std::remove_cvref_t<decltype(socket_impl->local_endpoint())>;
108 :
109 302 : endpoint_type local_ep{};
110 302 : sockaddr_storage local_storage{};
111 302 : socklen_t local_len = sizeof(local_storage);
112 302 : if (::getsockname(
113 302 : fd, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
114 302 : local_ep = from_sockaddr_as(local_storage, local_len, endpoint_type{});
115 :
116 302 : endpoint_type remote_ep{};
117 302 : sockaddr_storage peer_storage{};
118 302 : socklen_t peer_len = sizeof(peer_storage);
119 302 : if (::getpeername(
120 302 : fd, reinterpret_cast<sockaddr*>(&peer_storage), &peer_len) == 0)
121 296 : remote_ep = from_sockaddr_as(peer_storage, peer_len, endpoint_type{});
122 :
123 302 : socket_impl->set_endpoints(local_ep, remote_ep);
124 :
125 302 : return {};
126 : }
127 :
128 : template<class Traits, class AccFinal>
129 : std::error_code
130 709 : do_open_acceptor(
131 : AccFinal* acc_impl, int family, int type, int protocol, bool is_ip) noexcept
132 : {
133 709 : acc_impl->close_socket();
134 :
135 709 : int fd = Traits::create_socket(family, type, protocol);
136 709 : if (fd < 0)
137 9 : return make_err(errno);
138 :
139 700 : std::error_code ec = is_ip ? Traits::configure_ip_acceptor(fd, family)
140 83 : : Traits::configure_local_socket(fd);
141 :
142 700 : if (ec)
143 : {
144 4 : ::close(fd);
145 4 : return ec;
146 : }
147 :
148 696 : acc_impl->init_acceptor_fd(fd);
149 696 : return {};
150 : }
151 :
152 : // Acceptor twin of do_assign_fd: always SOCK_STREAM, and refreshes
153 : // only the local endpoint because listeners have no peer. Listen
154 : // state is not verified; accept() surfaces the error naturally if
155 : // the descriptor is not listening.
156 : template<class Traits, class AccFinal>
157 : std::error_code
158 31 : do_assign_acceptor_fd(AccFinal* acc_impl, int fd, bool is_ip) noexcept
159 : {
160 31 : if (fd >= 0 && fd == acc_impl->native_handle())
161 4 : return std::make_error_code(std::errc::invalid_argument);
162 :
163 27 : if (auto ec = validate_socket_fd(fd, SOCK_STREAM, is_ip))
164 8 : return ec;
165 :
166 19 : if (auto ec = Traits::validate_assigned_fd(fd))
167 1 : return ec;
168 :
169 18 : acc_impl->close_socket();
170 :
171 18 : if (auto ec = acc_impl->init_and_register(fd))
172 1 : return ec;
173 :
174 : using endpoint_type =
175 : std::remove_cvref_t<decltype(acc_impl->local_endpoint())>;
176 :
177 17 : endpoint_type local_ep{};
178 17 : sockaddr_storage local_storage{};
179 17 : socklen_t local_len = sizeof(local_storage);
180 17 : if (::getsockname(
181 17 : fd, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
182 17 : local_ep = from_sockaddr_as(local_storage, local_len, endpoint_type{});
183 :
184 17 : acc_impl->set_local_endpoint(local_ep);
185 :
186 17 : return {};
187 : }
188 :
189 : // ============================================================
190 : // TCP service
191 : // ============================================================
192 :
193 : template<class Derived, class Traits, class SocketFinal>
194 : class reactor_tcp_service_impl
195 : : public reactor_socket_service<
196 : Derived,
197 : tcp_service,
198 : typename Traits::scheduler_type,
199 : SocketFinal>
200 : {
201 : using base_service = reactor_socket_service<
202 : Derived,
203 : tcp_service,
204 : typename Traits::scheduler_type,
205 : SocketFinal>;
206 : friend Derived;
207 : friend base_service;
208 :
209 2106 : explicit reactor_tcp_service_impl(capy::execution_context& ctx)
210 2106 : : base_service(ctx)
211 : {
212 2106 : }
213 :
214 : public:
215 : static constexpr bool needs_write_notification =
216 : Traits::needs_write_notification;
217 :
218 4617 : std::error_code open_socket(
219 : tcp_socket::implementation& impl,
220 : int family,
221 : int type,
222 : int protocol) override
223 : {
224 4617 : return do_open_socket<Traits>(
225 4617 : static_cast<SocketFinal*>(&impl), family, type, protocol, true);
226 : }
227 :
228 29 : std::error_code assign_socket(
229 : tcp_socket::implementation& impl, native_handle_type fd) override
230 : {
231 29 : return do_assign_fd<Traits>(
232 29 : static_cast<SocketFinal*>(&impl), fd, SOCK_STREAM, true);
233 : }
234 :
235 : std::error_code
236 19 : bind_socket(tcp_socket::implementation& impl, endpoint ep) override
237 : {
238 19 : return static_cast<SocketFinal*>(&impl)->do_bind(ep);
239 : }
240 :
241 5 : void pre_shutdown(SocketFinal* impl) noexcept
242 : {
243 5 : impl->hook_.pre_shutdown(impl->native_handle());
244 5 : }
245 :
246 13774 : void pre_destroy(SocketFinal* impl) noexcept
247 : {
248 13774 : impl->hook_.pre_destroy(impl->native_handle());
249 13774 : }
250 : };
251 :
252 : // ============================================================
253 : // Local stream service
254 : // ============================================================
255 :
256 : template<class Derived, class Traits, class SocketFinal>
257 : class reactor_local_stream_service_impl
258 : : public reactor_socket_service<
259 : Derived,
260 : local_stream_service,
261 : typename Traits::scheduler_type,
262 : SocketFinal>
263 : {
264 : using base_service = reactor_socket_service<
265 : Derived,
266 : local_stream_service,
267 : typename Traits::scheduler_type,
268 : SocketFinal>;
269 : friend Derived;
270 : friend base_service;
271 :
272 2106 : explicit reactor_local_stream_service_impl(capy::execution_context& ctx)
273 2106 : : base_service(ctx)
274 : {
275 2106 : }
276 :
277 : public:
278 : static constexpr bool needs_write_notification =
279 : Traits::needs_write_notification;
280 :
281 51 : std::error_code open_socket(
282 : local_stream_socket::implementation& impl,
283 : int family,
284 : int type,
285 : int protocol) override
286 : {
287 51 : return do_open_socket<Traits>(
288 51 : static_cast<SocketFinal*>(&impl), family, type, protocol, false);
289 : }
290 :
291 173 : std::error_code assign_socket(
292 : local_stream_socket::implementation& impl,
293 : native_handle_type fd) override
294 : {
295 173 : return do_assign_fd<Traits>(
296 173 : static_cast<SocketFinal*>(&impl), fd, SOCK_STREAM, false);
297 : }
298 : };
299 :
300 : // ============================================================
301 : // UDP service
302 : // ============================================================
303 :
304 : template<class Derived, class Traits, class SocketFinal>
305 : class reactor_udp_service_impl
306 : : public reactor_socket_service<
307 : Derived,
308 : udp_service,
309 : typename Traits::scheduler_type,
310 : SocketFinal>
311 : {
312 : using base_service = reactor_socket_service<
313 : Derived,
314 : udp_service,
315 : typename Traits::scheduler_type,
316 : SocketFinal>;
317 : friend Derived;
318 : friend base_service;
319 :
320 2106 : explicit reactor_udp_service_impl(capy::execution_context& ctx)
321 2106 : : base_service(ctx)
322 : {
323 2106 : }
324 :
325 : public:
326 : static constexpr bool needs_write_notification =
327 : Traits::needs_write_notification;
328 :
329 319 : std::error_code open_datagram_socket(
330 : udp_socket::implementation& impl,
331 : int family,
332 : int type,
333 : int protocol) override
334 : {
335 319 : return do_open_socket<Traits>(
336 319 : static_cast<SocketFinal*>(&impl), family, type, protocol, true);
337 : }
338 :
339 22 : std::error_code assign_socket(
340 : udp_socket::implementation& impl, native_handle_type fd) override
341 : {
342 22 : return do_assign_fd<Traits>(
343 22 : static_cast<SocketFinal*>(&impl), fd, SOCK_DGRAM, true);
344 : }
345 :
346 : std::error_code
347 177 : bind_datagram(udp_socket::implementation& impl, endpoint ep) override
348 : {
349 177 : return static_cast<SocketFinal*>(&impl)->do_bind(ep);
350 : }
351 : };
352 :
353 : // ============================================================
354 : // Local datagram service
355 : // ============================================================
356 :
357 : template<class Derived, class Traits, class SocketFinal>
358 : class reactor_local_dgram_service_impl
359 : : public reactor_socket_service<
360 : Derived,
361 : local_datagram_service,
362 : typename Traits::scheduler_type,
363 : SocketFinal>
364 : {
365 : using base_service = reactor_socket_service<
366 : Derived,
367 : local_datagram_service,
368 : typename Traits::scheduler_type,
369 : SocketFinal>;
370 : friend Derived;
371 : friend base_service;
372 :
373 2106 : explicit reactor_local_dgram_service_impl(capy::execution_context& ctx)
374 2106 : : base_service(ctx)
375 : {
376 2106 : }
377 :
378 : public:
379 : static constexpr bool needs_write_notification =
380 : Traits::needs_write_notification;
381 :
382 91 : std::error_code open_socket(
383 : local_datagram_socket::implementation& impl,
384 : int family,
385 : int type,
386 : int protocol) override
387 : {
388 91 : return do_open_socket<Traits>(
389 91 : static_cast<SocketFinal*>(&impl), family, type, protocol, false);
390 : }
391 :
392 144 : std::error_code assign_socket(
393 : local_datagram_socket::implementation& impl,
394 : native_handle_type fd) override
395 : {
396 144 : return do_assign_fd<Traits>(
397 144 : static_cast<SocketFinal*>(&impl), fd, SOCK_DGRAM, false);
398 : }
399 :
400 68 : std::error_code bind_socket(
401 : local_datagram_socket::implementation& impl,
402 : corosio::local_endpoint ep) override
403 : {
404 68 : return static_cast<SocketFinal*>(&impl)->do_bind(ep);
405 : }
406 : };
407 :
408 : // ============================================================
409 : // Acceptor service
410 : // ============================================================
411 :
412 : template<
413 : class Derived,
414 : class Traits,
415 : class ServiceBase,
416 : class AccFinal,
417 : class StreamServiceFinal,
418 : class Endpoint>
419 : class reactor_acceptor_service_impl
420 : : public reactor_acceptor_service<
421 : Derived,
422 : ServiceBase,
423 : typename Traits::scheduler_type,
424 : AccFinal,
425 : StreamServiceFinal>
426 : {
427 : using base_service = reactor_acceptor_service<
428 : Derived,
429 : ServiceBase,
430 : typename Traits::scheduler_type,
431 : AccFinal,
432 : StreamServiceFinal>;
433 : friend Derived;
434 : friend base_service;
435 :
436 4212 : explicit reactor_acceptor_service_impl(capy::execution_context& ctx)
437 4212 : : base_service(ctx)
438 : {
439 : // Look up the concrete stream service directly by its type.
440 4212 : this->stream_svc_ =
441 4212 : this->ctx_.template find_service<StreamServiceFinal>();
442 4212 : }
443 :
444 : public:
445 709 : std::error_code open_acceptor_socket(
446 : typename AccFinal::impl_base_type& impl,
447 : int family,
448 : int type,
449 : int protocol) override
450 : {
451 709 : return do_open_acceptor<Traits>(
452 : static_cast<AccFinal*>(&impl), family, type, protocol,
453 709 : std::is_same_v<Endpoint, endpoint>);
454 : }
455 :
456 31 : std::error_code assign_socket(
457 : typename AccFinal::impl_base_type& impl, native_handle_type fd) override
458 : {
459 31 : return do_assign_acceptor_fd<Traits>(
460 : static_cast<AccFinal*>(&impl), fd,
461 31 : std::is_same_v<Endpoint, endpoint>);
462 : }
463 :
464 : std::error_code
465 672 : bind_acceptor(typename AccFinal::impl_base_type& impl, Endpoint ep) override
466 : {
467 672 : return static_cast<AccFinal*>(&impl)->do_bind(ep);
468 : }
469 :
470 628 : std::error_code listen_acceptor(
471 : typename AccFinal::impl_base_type& impl, int backlog) override
472 : {
473 628 : return static_cast<AccFinal*>(&impl)->do_listen(backlog);
474 : }
475 : };
476 :
477 : } // namespace boost::corosio::detail
478 :
479 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
|