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 7430 : do_open_socket(
50 : SocketFinal* socket_impl,
51 : int family, int type, int protocol,
52 : bool is_ip) noexcept
53 : {
54 7430 : socket_impl->close_socket();
55 :
56 7430 : int fd = Traits::create_socket(family, type, protocol);
57 7430 : if (fd < 0)
58 MIS 0 : return make_err(errno);
59 :
60 : std::error_code ec = is_ip
61 HIT 7430 : ? Traits::configure_ip_socket(fd, family)
62 117 : : Traits::configure_local_socket(fd);
63 :
64 7430 : if (ec)
65 : {
66 MIS 0 : ::close(fd);
67 0 : return ec;
68 : }
69 :
70 HIT 7430 : if (auto ec = socket_impl->init_and_register(fd))
71 : {
72 MIS 0 : ::close(fd);
73 0 : return ec;
74 : }
75 HIT 7430 : return {};
76 : }
77 :
78 : template<class Traits, class SocketFinal>
79 : std::error_code
80 256 : 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 256 : 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 252 : if (auto ec = validate_socket_fd(fd, expected_type, is_ip))
94 34 : 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 218 : if (auto ec = Traits::validate_assigned_fd(fd))
100 MIS 0 : return ec;
101 :
102 HIT 218 : socket_impl->close_socket();
103 :
104 218 : 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 217 : endpoint_type local_ep{};
112 217 : sockaddr_storage local_storage{};
113 217 : socklen_t local_len = sizeof(local_storage);
114 217 : if (::getsockname(
115 217 : fd, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
116 217 : local_ep = from_sockaddr_as(local_storage, local_len, endpoint_type{});
117 :
118 217 : endpoint_type remote_ep{};
119 217 : sockaddr_storage peer_storage{};
120 217 : socklen_t peer_len = sizeof(peer_storage);
121 217 : if (::getpeername(
122 217 : fd, reinterpret_cast<sockaddr*>(&peer_storage), &peer_len) == 0)
123 211 : remote_ep = from_sockaddr_as(peer_storage, peer_len, endpoint_type{});
124 :
125 217 : socket_impl->set_endpoints(local_ep, remote_ep);
126 :
127 217 : return {};
128 : }
129 :
130 : template<class Traits, class AccFinal>
131 : std::error_code
132 487 : do_open_acceptor(
133 : AccFinal* acc_impl,
134 : int family, int type, int protocol,
135 : bool is_ip) noexcept
136 : {
137 487 : acc_impl->close_socket();
138 :
139 487 : int fd = Traits::create_socket(family, type, protocol);
140 487 : if (fd < 0)
141 MIS 0 : return make_err(errno);
142 :
143 : std::error_code ec = is_ip
144 HIT 487 : ? Traits::configure_ip_acceptor(fd, family)
145 70 : : Traits::configure_local_socket(fd);
146 :
147 487 : if (ec)
148 : {
149 MIS 0 : ::close(fd);
150 0 : return ec;
151 : }
152 :
153 HIT 487 : acc_impl->init_acceptor_fd(fd);
154 487 : 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 1603 : explicit reactor_tcp_service_impl(capy::execution_context& ctx)
213 1603 : : base_service(ctx) {}
214 :
215 : public:
216 : static constexpr bool needs_write_notification =
217 : Traits::needs_write_notification;
218 :
219 7082 : std::error_code open_socket(
220 : tcp_socket::implementation& impl,
221 : int family, int type, int protocol) override
222 : {
223 7082 : return do_open_socket<Traits>(
224 : static_cast<SocketFinal*>(&impl),
225 7082 : family, type, protocol, true);
226 : }
227 :
228 18 : std::error_code assign_socket(
229 : tcp_socket::implementation& impl, native_handle_type fd) override
230 : {
231 18 : return do_assign_fd<Traits>(
232 18 : 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 21265 : void pre_destroy(SocketFinal* impl) noexcept
247 : {
248 21265 : impl->hook_.pre_destroy(impl->native_handle());
249 21265 : }
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 1603 : explicit reactor_local_stream_service_impl(capy::execution_context& ctx)
271 1603 : : 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 114 : std::error_code assign_socket(
287 : local_stream_socket::implementation& impl,
288 : native_handle_type fd) override
289 : {
290 114 : return do_assign_fd<Traits>(
291 114 : 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 1603 : explicit reactor_udp_service_impl(capy::execution_context& ctx)
314 1603 : : base_service(ctx) {}
315 :
316 : public:
317 : static constexpr bool needs_write_notification =
318 : Traits::needs_write_notification;
319 :
320 231 : std::error_code open_datagram_socket(
321 : udp_socket::implementation& impl,
322 : int family, int type, int protocol) override
323 : {
324 231 : return do_open_socket<Traits>(
325 : static_cast<SocketFinal*>(&impl),
326 231 : family, type, protocol, true);
327 : }
328 :
329 18 : std::error_code assign_socket(
330 : udp_socket::implementation& impl, native_handle_type fd) override
331 : {
332 18 : return do_assign_fd<Traits>(
333 18 : 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 1603 : explicit reactor_local_dgram_service_impl(capy::execution_context& ctx)
362 1603 : : base_service(ctx) {}
363 :
364 : public:
365 : static constexpr bool needs_write_notification =
366 : Traits::needs_write_notification;
367 :
368 72 : std::error_code open_socket(
369 : local_datagram_socket::implementation& impl,
370 : int family, int type, int protocol) override
371 : {
372 72 : return do_open_socket<Traits>(
373 : static_cast<SocketFinal*>(&impl),
374 72 : 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 3206 : explicit reactor_acceptor_service_impl(capy::execution_context& ctx)
417 3206 : : base_service(ctx)
418 : {
419 : // Look up the concrete stream service directly by its type.
420 3206 : this->stream_svc_ =
421 3206 : this->ctx_.template find_service<StreamServiceFinal>();
422 3206 : }
423 :
424 : public:
425 487 : std::error_code open_acceptor_socket(
426 : typename AccFinal::impl_base_type& impl,
427 : int family, int type, int protocol) override
428 : {
429 487 : return do_open_acceptor<Traits>(
430 : static_cast<AccFinal*>(&impl),
431 : family, type, protocol,
432 487 : 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 469 : std::error_code bind_acceptor(
445 : typename AccFinal::impl_base_type& impl,
446 : Endpoint ep) override
447 : {
448 469 : return static_cast<AccFinal*>(&impl)->do_bind(ep);
449 : }
450 :
451 424 : std::error_code listen_acceptor(
452 : typename AccFinal::impl_base_type& impl,
453 : int backlog) override
454 : {
455 424 : 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
|