TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
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_SELECT_SELECT_UDP_SERVICE_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
12 :
13 : #include <boost/corosio/detail/platform.hpp>
14 :
15 : #if BOOST_COROSIO_HAS_SELECT
16 :
17 : #include <boost/corosio/detail/config.hpp>
18 : #include <boost/corosio/detail/udp_service.hpp>
19 :
20 : #include <boost/corosio/native/detail/select/select_udp_socket.hpp>
21 : #include <boost/corosio/native/detail/select/select_scheduler.hpp>
22 : #include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
23 :
24 : #include <boost/corosio/native/detail/reactor/reactor_op_complete.hpp>
25 :
26 : #include <coroutine>
27 : #include <mutex>
28 :
29 : #include <errno.h>
30 : #include <fcntl.h>
31 : #include <netinet/in.h>
32 : #include <sys/select.h>
33 : #include <sys/socket.h>
34 : #include <unistd.h>
35 :
36 : namespace boost::corosio::detail {
37 :
38 : /** select UDP service implementation.
39 :
40 : Inherits from udp_service to enable runtime polymorphism.
41 : Uses key_type = udp_service for service lookup.
42 : */
43 : class BOOST_COROSIO_DECL select_udp_service final
44 : : public reactor_socket_service<
45 : select_udp_service,
46 : udp_service,
47 : select_scheduler,
48 : select_udp_socket>
49 : {
50 : public:
51 HIT 228 : explicit select_udp_service(capy::execution_context& ctx)
52 228 : : reactor_socket_service(ctx)
53 : {
54 228 : }
55 :
56 : std::error_code open_datagram_socket(
57 : udp_socket::implementation& impl,
58 : int family,
59 : int type,
60 : int protocol) override;
61 : std::error_code
62 : bind_datagram(udp_socket::implementation& impl, endpoint ep) override;
63 : };
64 :
65 : // Cancellation for connectionless ops
66 :
67 : inline void
68 MIS 0 : select_send_to_op::cancel() noexcept
69 : {
70 0 : if (socket_impl_)
71 0 : socket_impl_->cancel_single_op(*this);
72 : else
73 0 : request_cancel();
74 0 : }
75 :
76 : inline void
77 HIT 1 : select_recv_from_op::cancel() noexcept
78 : {
79 1 : if (socket_impl_)
80 1 : socket_impl_->cancel_single_op(*this);
81 : else
82 MIS 0 : request_cancel();
83 HIT 1 : }
84 :
85 : // Cancellation for connected-mode ops
86 :
87 : inline void
88 MIS 0 : select_udp_connect_op::cancel() noexcept
89 : {
90 0 : if (socket_impl_)
91 0 : socket_impl_->cancel_single_op(*this);
92 : else
93 0 : request_cancel();
94 0 : }
95 :
96 : inline void
97 0 : select_send_op::cancel() noexcept
98 : {
99 0 : if (socket_impl_)
100 0 : socket_impl_->cancel_single_op(*this);
101 : else
102 0 : request_cancel();
103 0 : }
104 :
105 : inline void
106 0 : select_recv_op::cancel() noexcept
107 : {
108 0 : if (socket_impl_)
109 0 : socket_impl_->cancel_single_op(*this);
110 : else
111 0 : request_cancel();
112 0 : }
113 :
114 : // Completion handlers
115 :
116 : inline void
117 HIT 8 : select_datagram_op::operator()()
118 : {
119 8 : complete_io_op(*this);
120 8 : }
121 :
122 : inline void
123 7 : select_recv_from_op::operator()()
124 : {
125 7 : complete_datagram_op(*this, this->source_out);
126 7 : }
127 :
128 : inline void
129 5 : select_udp_connect_op::operator()()
130 : {
131 5 : complete_connect_op(*this);
132 5 : }
133 :
134 : inline void
135 2 : select_recv_op::operator()()
136 : {
137 2 : complete_io_op(*this);
138 2 : }
139 :
140 : // Socket construction/destruction
141 :
142 43 : inline select_udp_socket::select_udp_socket(select_udp_service& svc) noexcept
143 43 : : reactor_datagram_socket(svc)
144 : {
145 43 : }
146 :
147 43 : inline select_udp_socket::~select_udp_socket() = default;
148 :
149 : // Connectionless I/O
150 :
151 : inline std::coroutine_handle<>
152 11 : select_udp_socket::send_to(
153 : std::coroutine_handle<> h,
154 : capy::executor_ref ex,
155 : buffer_param buf,
156 : endpoint dest,
157 : int flags,
158 : std::stop_token token,
159 : std::error_code* ec,
160 : std::size_t* bytes_out)
161 : {
162 11 : auto result = do_send_to(h, ex, buf, dest, flags, token, ec, bytes_out);
163 11 : if (result == std::noop_coroutine())
164 8 : svc_.scheduler().notify_reactor();
165 11 : return result;
166 : }
167 :
168 : inline std::coroutine_handle<>
169 16 : select_udp_socket::recv_from(
170 : std::coroutine_handle<> h,
171 : capy::executor_ref ex,
172 : buffer_param buf,
173 : endpoint* source,
174 : int flags,
175 : std::stop_token token,
176 : std::error_code* ec,
177 : std::size_t* bytes_out)
178 : {
179 16 : return do_recv_from(h, ex, buf, source, flags, token, ec, bytes_out);
180 : }
181 :
182 : // Connected-mode I/O
183 :
184 : inline std::coroutine_handle<>
185 6 : select_udp_socket::connect(
186 : std::coroutine_handle<> h,
187 : capy::executor_ref ex,
188 : endpoint ep,
189 : std::stop_token token,
190 : std::error_code* ec)
191 : {
192 6 : auto result = do_connect(h, ex, ep, token, ec);
193 6 : if (result == std::noop_coroutine())
194 5 : svc_.scheduler().notify_reactor();
195 6 : return result;
196 : }
197 :
198 : inline std::coroutine_handle<>
199 3 : select_udp_socket::send(
200 : std::coroutine_handle<> h,
201 : capy::executor_ref ex,
202 : buffer_param buf,
203 : int flags,
204 : std::stop_token token,
205 : std::error_code* ec,
206 : std::size_t* bytes_out)
207 : {
208 3 : auto result = do_send(h, ex, buf, flags, token, ec, bytes_out);
209 3 : if (result == std::noop_coroutine())
210 MIS 0 : svc_.scheduler().notify_reactor();
211 HIT 3 : return result;
212 : }
213 :
214 : inline std::coroutine_handle<>
215 2 : select_udp_socket::recv(
216 : std::coroutine_handle<> h,
217 : capy::executor_ref ex,
218 : buffer_param buf,
219 : int flags,
220 : std::stop_token token,
221 : std::error_code* ec,
222 : std::size_t* bytes_out)
223 : {
224 2 : return do_recv(h, ex, buf, flags, token, ec, bytes_out);
225 : }
226 :
227 : inline endpoint
228 2 : select_udp_socket::remote_endpoint() const noexcept
229 : {
230 2 : return reactor_datagram_socket::remote_endpoint();
231 : }
232 :
233 : inline void
234 2 : select_udp_socket::cancel() noexcept
235 : {
236 2 : do_cancel();
237 2 : }
238 :
239 : inline void
240 166 : select_udp_socket::close_socket() noexcept
241 : {
242 166 : do_close_socket();
243 166 : }
244 :
245 : inline std::error_code
246 40 : select_udp_service::open_datagram_socket(
247 : udp_socket::implementation& impl, int family, int type, int protocol)
248 : {
249 40 : auto* select_impl = static_cast<select_udp_socket*>(&impl);
250 40 : select_impl->close_socket();
251 :
252 40 : int fd = ::socket(family, type, protocol);
253 40 : if (fd < 0)
254 MIS 0 : return make_err(errno);
255 :
256 HIT 40 : if (family == AF_INET6)
257 : {
258 7 : int one = 1;
259 7 : ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
260 : }
261 :
262 40 : int flags = ::fcntl(fd, F_GETFL, 0);
263 40 : if (flags == -1)
264 : {
265 MIS 0 : int errn = errno;
266 0 : ::close(fd);
267 0 : return make_err(errn);
268 : }
269 HIT 40 : if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
270 : {
271 MIS 0 : int errn = errno;
272 0 : ::close(fd);
273 0 : return make_err(errn);
274 : }
275 HIT 40 : if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
276 : {
277 MIS 0 : int errn = errno;
278 0 : ::close(fd);
279 0 : return make_err(errn);
280 : }
281 :
282 HIT 40 : if (fd >= FD_SETSIZE)
283 : {
284 MIS 0 : ::close(fd);
285 0 : return make_err(EMFILE);
286 : }
287 :
288 : #ifdef SO_NOSIGPIPE
289 : {
290 : int one = 1;
291 : ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
292 : }
293 : #endif
294 :
295 HIT 40 : select_impl->fd_ = fd;
296 :
297 40 : select_impl->desc_state_.fd = fd;
298 : {
299 40 : std::lock_guard lock(select_impl->desc_state_.mutex);
300 40 : select_impl->desc_state_.read_op = nullptr;
301 40 : select_impl->desc_state_.write_op = nullptr;
302 40 : select_impl->desc_state_.connect_op = nullptr;
303 40 : }
304 40 : scheduler().register_descriptor(fd, &select_impl->desc_state_);
305 :
306 40 : return {};
307 : }
308 :
309 : inline std::error_code
310 24 : select_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
311 : {
312 24 : return static_cast<select_udp_socket*>(&impl)->do_bind(ep);
313 : }
314 :
315 : } // namespace boost::corosio::detail
316 :
317 : #endif // BOOST_COROSIO_HAS_SELECT
318 :
319 : #endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
|