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_EPOLL_EPOLL_TRAITS_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP
12 :
13 : #include <boost/corosio/detail/platform.hpp>
14 :
15 : #if BOOST_COROSIO_HAS_EPOLL
16 :
17 : #include <boost/corosio/native/detail/make_err.hpp>
18 : #include <boost/corosio/native/detail/reactor/reactor_descriptor_ops.hpp>
19 : #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
20 :
21 : #include <system_error>
22 : #include <tuple>
23 :
24 : #include <errno.h>
25 : #include <netinet/in.h>
26 : #include <sys/socket.h>
27 : #include <sys/uio.h>
28 : #include <unistd.h>
29 :
30 : /* epoll backend traits.
31 :
32 : Captures the platform-specific behavior of the Linux epoll backend:
33 : atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for
34 : accepted connections, and sendmsg(MSG_NOSIGNAL) for writes.
35 : */
36 :
37 : namespace boost::corosio::detail {
38 :
39 : class epoll_scheduler;
40 :
41 : struct epoll_traits
42 : {
43 : using scheduler_type = epoll_scheduler;
44 : using desc_state_type = reactor_descriptor_state;
45 :
46 : static constexpr bool needs_park_notification = false;
47 :
48 : // No extra per-socket state or lifecycle hooks needed for epoll.
49 : struct stream_socket_hook
50 : {
51 HIT 197 : std::error_code on_set_option(
52 : int fd,
53 : int level,
54 : int optname,
55 : void const* data,
56 : std::size_t size) noexcept
57 : {
58 197 : if (::setsockopt(
59 197 : fd, level, optname, data, static_cast<socklen_t>(size)) !=
60 : 0)
61 5 : return make_err(errno);
62 192 : return {};
63 : }
64 23373 : static void pre_shutdown(int) noexcept {}
65 7628 : static void pre_destroy(int) noexcept {}
66 : };
67 :
68 : struct write_policy
69 : {
70 77 : static ssize_t write(int fd, iovec* iovecs, int count) noexcept
71 : {
72 77 : msghdr msg{};
73 77 : msg.msg_iov = iovecs;
74 77 : msg.msg_iovlen = static_cast<std::size_t>(count);
75 :
76 : ssize_t n;
77 : do
78 : {
79 78 : n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
80 : }
81 78 : while (n < 0 && errno == EINTR);
82 77 : return n;
83 : }
84 :
85 : static ssize_t
86 99522 : write_one(int fd, void const* data, std::size_t size) noexcept
87 : {
88 : ssize_t n;
89 : do
90 : {
91 99523 : n = ::send(fd, data, size, MSG_NOSIGNAL);
92 : }
93 99523 : while (n < 0 && errno == EINTR);
94 99522 : return n;
95 : }
96 : };
97 :
98 : using descriptor_write_policy = detail::descriptor_write_policy;
99 :
100 : struct accept_policy
101 : {
102 : static int
103 5055 : do_accept(int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept
104 : {
105 5055 : addrlen = sizeof(peer);
106 : int new_fd;
107 : do
108 : {
109 5056 : new_fd = ::accept4(
110 : fd, reinterpret_cast<sockaddr*>(&peer), &addrlen,
111 : SOCK_NONBLOCK | SOCK_CLOEXEC);
112 : }
113 5056 : while (new_fd < 0 && errno == EINTR);
114 5055 : return new_fd;
115 : }
116 : };
117 :
118 : // Create a nonblocking, close-on-exec socket using Linux's atomic flags.
119 3185 : static int create_socket(int family, int type, int protocol) noexcept
120 : {
121 3185 : return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
122 : }
123 :
124 : // Apply protocol-specific options after socket creation.
125 : // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort).
126 2703 : static std::error_code configure_ip_socket(int fd, int family) noexcept
127 : {
128 2703 : if (family == AF_INET6)
129 : {
130 23 : int one = 1;
131 : std::ignore =
132 23 : ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
133 : }
134 2703 : return {};
135 : }
136 :
137 : // Apply protocol-specific options for acceptor sockets.
138 : // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort).
139 356 : static std::error_code configure_ip_acceptor(int fd, int family) noexcept
140 : {
141 356 : if (family == AF_INET6)
142 : {
143 11 : int val = 0;
144 : std::ignore =
145 11 : ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
146 : }
147 356 : return {};
148 : }
149 :
150 : // No extra configuration needed for local (unix) sockets on epoll.
151 117 : static std::error_code configure_local_socket(int /*fd*/) noexcept
152 : {
153 117 : return {};
154 : }
155 :
156 : // Non-mutating validation for fds adopted via assign(). Used when
157 : // the caller retains fd ownership responsibility.
158 164 : static std::error_code validate_assigned_fd(int /*fd*/) noexcept
159 : {
160 164 : return {};
161 : }
162 : };
163 :
164 : } // namespace boost::corosio::detail
165 :
166 : #endif // BOOST_COROSIO_HAS_EPOLL
167 :
168 : #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP
|