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