include/boost/corosio/native/detail/kqueue/kqueue_traits.hpp

77.5% Lines (86/111) 92.9% List of functions (13/14) 41.8% Branches (46/110)
kqueue_traits.hpp
f(x) Functions (14)
Function Calls Lines Branches Blocks
boost::corosio::detail::kqueue_traits::stream_socket_hook::stream_socket_hook() :56 20798x 100.0% 100.0% boost::corosio::detail::kqueue_traits::stream_socket_hook::on_set_option(int, int, int, void const*, unsigned long) :60 1130x 90.9% 58.3% 72.0% boost::corosio::detail::kqueue_traits::stream_socket_hook::pre_shutdown(int) :77 31146x 100.0% 100.0% boost::corosio::detail::kqueue_traits::stream_socket_hook::pre_destroy(int) :82 10382x 100.0% 100.0% boost::corosio::detail::kqueue_traits::stream_socket_hook::reset_linger(int) :88 41528x 100.0% 66.7% 83.0% boost::corosio::detail::kqueue_traits::write_policy::write(int, iovec*, int) :103 0 0.0% 0.0% 0.0% boost::corosio::detail::kqueue_traits::write_policy::write_one(int, void const*, unsigned long) :117 237022x 100.0% 66.7% 88.0% boost::corosio::detail::kqueue_traits::accept_policy::do_accept(int, sockaddr_storage&, unsigned int&) :132 6855x 61.3% 33.3% 59.0% boost::corosio::detail::kqueue_traits::create_socket(int, int, int) :186 4204x 100.0% 50.0% 66.0% boost::corosio::detail::kqueue_traits::set_fd_options(int) :193 4204x 69.2% 33.3% 52.0% boost::corosio::detail::kqueue_traits::configure_ip_socket(int, int) :216 3499x 90.9% 66.7% 75.0% boost::corosio::detail::kqueue_traits::configure_ip_acceptor(int, int) :235 687x 90.9% 66.7% 75.0% boost::corosio::detail::kqueue_traits::configure_local_socket(int) :252 18x 100.0% 100.0% boost::corosio::detail::kqueue_traits::validate_assigned_fd(int) :260 14x 100.0% 100.0%
Line Branch TLA Hits 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_KQUEUE_KQUEUE_TRAITS_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TRAITS_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_HAS_KQUEUE
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
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <netinet/in.h>
25 #include <sys/socket.h>
26 #include <sys/uio.h>
27 #include <unistd.h>
28
29 /* kqueue backend traits.
30
31 Captures the platform-specific behavior of the BSD/macOS kqueue backend:
32 manual fcntl for O_NONBLOCK/FD_CLOEXEC, mandatory SO_NOSIGPIPE (macOS
33 lacks MSG_NOSIGNAL), writev() for writes, and accept()+fcntl for
34 accepted connections.
35 */
36
37 namespace boost::corosio::detail {
38
39 class kqueue_scheduler;
40
41 struct kqueue_traits
42 {
43 using scheduler_type = kqueue_scheduler;
44 using desc_state_type = reactor_descriptor_state;
45
46 static constexpr bool needs_write_notification = false;
47
48 /* macOS kqueue workaround: RST doesn't reliably trigger EV_EOF.
49 If the user sets SO_LINGER, we clear it before close so the
50 destructor doesn't block and close() sends FIN instead of RST.
51
52 The hook tracks whether the user explicitly set SO_LINGER via
53 set_option(). On pre_shutdown/pre_destroy, if the flag is set,
54 we reset linger to off before the fd is closed.
55 */
56 10399x struct stream_socket_hook
57 {
58 10399x bool user_set_linger_ = false;
59
60 1130x std::error_code on_set_option(
61 int fd, int level, int optname,
62 void const* data, std::size_t size) noexcept
63 {
64
2/4
✓ Branch 0 taken 1130 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1130 times.
1130x if (::setsockopt(
65 1130x fd, level, optname, data,
66 1130x static_cast<socklen_t>(size)) != 0)
67 return make_err(errno);
68
69
5/6
✓ Branch 0 taken 1117 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 1109 times.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1109 times.
1130x if (level == SOL_SOCKET && optname == SO_LINGER &&
70 1109x size >= sizeof(struct ::linger))
71 1109x user_set_linger_ =
72 1109x static_cast<struct ::linger const*>(data)->l_onoff != 0;
73
74 1130x return {};
75 1130x }
76
77 31146x void pre_shutdown(int fd) noexcept
78 {
79 31146x reset_linger(fd);
80 31146x }
81
82 10382x void pre_destroy(int fd) noexcept
83 {
84 10382x reset_linger(fd);
85 10382x }
86
87 private:
88 41528x void reset_linger(int fd) noexcept
89 {
90
3/4
✓ Branch 0 taken 1107 times.
✓ Branch 1 taken 40421 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1107 times.
41528x if (user_set_linger_ && fd >= 0)
91 {
92 struct ::linger lg;
93 1107x lg.l_onoff = 0;
94 1107x lg.l_linger = 0;
95
1/2
✓ Branch 0 taken 1107 times.
✗ Branch 1 not taken.
1107x ::setsockopt(fd, SOL_SOCKET, SO_LINGER, &lg, sizeof(lg));
96 1107x }
97 41528x user_set_linger_ = false;
98 41528x }
99 };
100
101 struct write_policy
102 {
103 static ssize_t write(int fd, iovec* iovecs, int count) noexcept
104 {
105 ssize_t n;
106 do
107 {
108 n = ::writev(fd, iovecs, count);
109 }
110 while (n < 0 && errno == EINTR);
111 return n;
112 }
113
114 // Single-buffer fast path. macOS lacks MSG_NOSIGNAL; SIGPIPE is
115 // suppressed by the mandatory SO_NOSIGPIPE set in accept_policy
116 // and set_fd_options, so plain write() is safe here.
117 237022x static ssize_t write_one(
118 int fd, void const* data, std::size_t size) noexcept
119 {
120 ssize_t n;
121 237022x do
122 {
123
1/2
✓ Branch 0 taken 237022 times.
✗ Branch 1 not taken.
237022x n = ::write(fd, data, size);
124 474044x }
125
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 237021 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
237022x while (n < 0 && errno == EINTR);
126 237022x return n;
127 }
128 };
129
130 struct accept_policy
131 {
132 6855x static int do_accept(
133 int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept
134 {
135 int new_fd;
136 6855x do
137 {
138 6855x addrlen = sizeof(peer);
139
1/2
✓ Branch 0 taken 6855 times.
✗ Branch 1 not taken.
6855x new_fd = ::accept(
140 6855x fd, reinterpret_cast<sockaddr*>(&peer), &addrlen);
141 13710x }
142
3/4
✓ Branch 0 taken 3432 times.
✓ Branch 1 taken 3423 times.
✓ Branch 2 taken 3432 times.
✗ Branch 3 not taken.
6855x while (new_fd < 0 && errno == EINTR);
143
144
2/2
✓ Branch 0 taken 3432 times.
✓ Branch 1 taken 3423 times.
6855x if (new_fd < 0)
145 3432x return new_fd;
146
147
1/2
✓ Branch 0 taken 3423 times.
✗ Branch 1 not taken.
3423x int flags = ::fcntl(new_fd, F_GETFL, 0);
148
2/4
✓ Branch 0 taken 3423 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3423 times.
✗ Branch 3 not taken.
6846x if (flags == -1 ||
149
1/2
✓ Branch 0 taken 3423 times.
✗ Branch 1 not taken.
3423x ::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1)
150 {
151 int err = errno;
152 ::close(new_fd);
153 errno = err;
154 return -1;
155 }
156
157
2/4
✓ Branch 0 taken 3423 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3423 times.
3423x if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1)
158 {
159 int err = errno;
160 ::close(new_fd);
161 errno = err;
162 return -1;
163 }
164
165 #ifndef BOOST_COROSIO_MRDOCS
166 // SO_NOSIGPIPE is mandatory on kqueue platforms (macOS lacks
167 // MSG_NOSIGNAL). Skipped under MRDOCS so the docs build can
168 // parse this header on Linux, where SO_NOSIGPIPE is absent.
169 3423x int one = 1;
170
2/4
✓ Branch 0 taken 3423 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3423 times.
3423x if (::setsockopt(
171 3423x new_fd, SOL_SOCKET, SO_NOSIGPIPE,
172 3423x &one, sizeof(one)) == -1)
173 {
174 int err = errno;
175 ::close(new_fd);
176 errno = err;
177 return -1;
178 }
179 #endif
180
181 3423x return new_fd;
182 6855x }
183 };
184
185 // Create a plain socket. Fd options are applied by configure_*().
186 4204x static int create_socket(int family, int type, int protocol) noexcept
187 {
188
1/2
✓ Branch 0 taken 4204 times.
✗ Branch 1 not taken.
4204x return ::socket(family, type, protocol);
189 }
190
191 // Set O_NONBLOCK, FD_CLOEXEC, and SO_NOSIGPIPE on a new fd.
192 // Caller is responsible for closing fd on error.
193 4204x static std::error_code set_fd_options(int fd) noexcept
194 {
195
1/2
✓ Branch 0 taken 4204 times.
✗ Branch 1 not taken.
4204x int flags = ::fcntl(fd, F_GETFL, 0);
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4204 times.
4204x if (flags == -1)
197 return make_err(errno);
198
2/4
✓ Branch 0 taken 4204 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4204 times.
4204x if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
199 return make_err(errno);
200
2/4
✓ Branch 0 taken 4204 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4204 times.
4204x if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
201 return make_err(errno);
202
203 #ifndef BOOST_COROSIO_MRDOCS
204 // SO_NOSIGPIPE is mandatory on kqueue platforms (see accept_policy).
205 4204x int one = 1;
206
2/4
✓ Branch 0 taken 4204 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4204 times.
4204x if (::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) != 0)
207 return make_err(errno);
208 #endif
209
210 4204x return {};
211 4204x }
212
213 // Apply protocol-specific options after socket creation.
214 // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort).
215 static std::error_code
216 3499x configure_ip_socket(int fd, int family) noexcept
217 {
218 3499x auto ec = set_fd_options(fd);
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3499 times.
3499x if (ec)
220 return ec;
221
222
2/2
✓ Branch 0 taken 3485 times.
✓ Branch 1 taken 14 times.
3499x if (family == AF_INET6)
223 {
224 14x int v6only = 1;
225
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14x (void)::setsockopt(
226 14x fd, IPPROTO_IPV6, IPV6_V6ONLY,
227 &v6only, sizeof(v6only));
228 14x }
229 3499x return {};
230 3499x }
231
232 // Apply protocol-specific options for acceptor sockets.
233 // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort).
234 static std::error_code
235 687x configure_ip_acceptor(int fd, int family) noexcept
236 {
237 687x auto ec = set_fd_options(fd);
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 687 times.
687x if (ec)
239 return ec;
240
241
2/2
✓ Branch 0 taken 678 times.
✓ Branch 1 taken 9 times.
687x if (family == AF_INET6)
242 {
243 9x int val = 0;
244
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9x (void)::setsockopt(
245 9x fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
246 9x }
247 687x return {};
248 687x }
249
250 // Apply options for local (unix) sockets.
251 static std::error_code
252 18x configure_local_socket(int fd) noexcept
253 {
254 18x return set_fd_options(fd);
255 }
256
257 // Non-mutating validation for fds adopted via assign(). Used when
258 // the caller retains fd ownership responsibility.
259 static std::error_code
260 14x validate_assigned_fd(int /*fd*/) noexcept
261 {
262 14x return {};
263 }
264 };
265
266 } // namespace boost::corosio::detail
267
268 #endif // BOOST_COROSIO_HAS_KQUEUE
269
270 #endif // BOOST_COROSIO_NATIVE_DETAIL_KQUEUE_KQUEUE_TRAITS_HPP
271