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