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 195 : explicit select_udp_service(capy::execution_context& ctx)
52 195 : : reactor_socket_service(ctx)
53 : {
54 195 : }
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 39 : inline select_udp_socket::select_udp_socket(select_udp_service& svc) noexcept
143 39 : : reactor_datagram_socket(svc)
144 : {
145 39 : }
146 :
147 39 : 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 : std::stop_token token,
158 : std::error_code* ec,
159 : std::size_t* bytes_out)
160 : {
161 11 : auto result = do_send_to(h, ex, buf, dest, token, ec, bytes_out);
162 11 : if (result == std::noop_coroutine())
163 8 : svc_.scheduler().notify_reactor();
164 11 : return result;
165 : }
166 :
167 : inline std::coroutine_handle<>
168 16 : select_udp_socket::recv_from(
169 : std::coroutine_handle<> h,
170 : capy::executor_ref ex,
171 : buffer_param buf,
172 : endpoint* source,
173 : std::stop_token token,
174 : std::error_code* ec,
175 : std::size_t* bytes_out)
176 : {
177 16 : return do_recv_from(h, ex, buf, source, token, ec, bytes_out);
178 : }
179 :
180 : // Connected-mode I/O
181 :
182 : inline std::coroutine_handle<>
183 6 : select_udp_socket::connect(
184 : std::coroutine_handle<> h,
185 : capy::executor_ref ex,
186 : endpoint ep,
187 : std::stop_token token,
188 : std::error_code* ec)
189 : {
190 6 : auto result = do_connect(h, ex, ep, token, ec);
191 6 : if (result == std::noop_coroutine())
192 5 : svc_.scheduler().notify_reactor();
193 6 : return result;
194 : }
195 :
196 : inline std::coroutine_handle<>
197 3 : select_udp_socket::send(
198 : std::coroutine_handle<> h,
199 : capy::executor_ref ex,
200 : buffer_param buf,
201 : std::stop_token token,
202 : std::error_code* ec,
203 : std::size_t* bytes_out)
204 : {
205 3 : auto result = do_send(h, ex, buf, token, ec, bytes_out);
206 3 : if (result == std::noop_coroutine())
207 MIS 0 : svc_.scheduler().notify_reactor();
208 HIT 3 : return result;
209 : }
210 :
211 : inline std::coroutine_handle<>
212 2 : select_udp_socket::recv(
213 : std::coroutine_handle<> h,
214 : capy::executor_ref ex,
215 : buffer_param buf,
216 : std::stop_token token,
217 : std::error_code* ec,
218 : std::size_t* bytes_out)
219 : {
220 2 : return do_recv(h, ex, buf, token, ec, bytes_out);
221 : }
222 :
223 : inline endpoint
224 2 : select_udp_socket::remote_endpoint() const noexcept
225 : {
226 2 : return reactor_datagram_socket::remote_endpoint();
227 : }
228 :
229 : inline void
230 2 : select_udp_socket::cancel() noexcept
231 : {
232 2 : do_cancel();
233 2 : }
234 :
235 : inline void
236 152 : select_udp_socket::close_socket() noexcept
237 : {
238 152 : do_close_socket();
239 152 : }
240 :
241 : inline std::error_code
242 37 : select_udp_service::open_datagram_socket(
243 : udp_socket::implementation& impl, int family, int type, int protocol)
244 : {
245 37 : auto* select_impl = static_cast<select_udp_socket*>(&impl);
246 37 : select_impl->close_socket();
247 :
248 37 : int fd = ::socket(family, type, protocol);
249 37 : if (fd < 0)
250 MIS 0 : return make_err(errno);
251 :
252 HIT 37 : if (family == AF_INET6)
253 : {
254 7 : int one = 1;
255 7 : ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
256 : }
257 :
258 37 : int flags = ::fcntl(fd, F_GETFL, 0);
259 37 : if (flags == -1)
260 : {
261 MIS 0 : int errn = errno;
262 0 : ::close(fd);
263 0 : return make_err(errn);
264 : }
265 HIT 37 : if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
266 : {
267 MIS 0 : int errn = errno;
268 0 : ::close(fd);
269 0 : return make_err(errn);
270 : }
271 HIT 37 : if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
272 : {
273 MIS 0 : int errn = errno;
274 0 : ::close(fd);
275 0 : return make_err(errn);
276 : }
277 :
278 HIT 37 : if (fd >= FD_SETSIZE)
279 : {
280 MIS 0 : ::close(fd);
281 0 : return make_err(EMFILE);
282 : }
283 :
284 : #ifdef SO_NOSIGPIPE
285 : {
286 : int one = 1;
287 : ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
288 : }
289 : #endif
290 :
291 HIT 37 : select_impl->fd_ = fd;
292 :
293 37 : select_impl->desc_state_.fd = fd;
294 : {
295 37 : std::lock_guard lock(select_impl->desc_state_.mutex);
296 37 : select_impl->desc_state_.read_op = nullptr;
297 37 : select_impl->desc_state_.write_op = nullptr;
298 37 : select_impl->desc_state_.connect_op = nullptr;
299 37 : }
300 37 : scheduler().register_descriptor(fd, &select_impl->desc_state_);
301 :
302 37 : return {};
303 : }
304 :
305 : inline std::error_code
306 21 : select_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
307 : {
308 21 : return static_cast<select_udp_socket*>(&impl)->do_bind(ep);
309 : }
310 :
311 : } // namespace boost::corosio::detail
312 :
313 : #endif // BOOST_COROSIO_HAS_SELECT
314 :
315 : #endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
|