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