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