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 +
    explicit select_udp_service(capy::execution_context& ctx)
 
52 +
        : reactor_socket_service(ctx)
 
53 +
    {
 
54 +
    }
 
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 +
select_send_to_op::cancel() noexcept
 
69 +
{
 
70 +
    if (socket_impl_)
 
71 +
        socket_impl_->cancel_single_op(*this);
 
72 +
    else
 
73 +
        request_cancel();
 
74 +
}
 
75 +

 
76 +
inline void
 
77 +
select_recv_from_op::cancel() noexcept
 
78 +
{
 
79 +
    if (socket_impl_)
 
80 +
        socket_impl_->cancel_single_op(*this);
 
81 +
    else
 
82 +
        request_cancel();
 
83 +
}
 
84 +

 
85 +
// Cancellation for connected-mode ops
 
86 +

 
87 +
inline void
 
88 +
select_udp_connect_op::cancel() noexcept
 
89 +
{
 
90 +
    if (socket_impl_)
 
91 +
        socket_impl_->cancel_single_op(*this);
 
92 +
    else
 
93 +
        request_cancel();
 
94 +
}
 
95 +

 
96 +
inline void
 
97 +
select_send_op::cancel() noexcept
 
98 +
{
 
99 +
    if (socket_impl_)
 
100 +
        socket_impl_->cancel_single_op(*this);
 
101 +
    else
 
102 +
        request_cancel();
 
103 +
}
 
104 +

 
105 +
inline void
 
106 +
select_recv_op::cancel() noexcept
 
107 +
{
 
108 +
    if (socket_impl_)
 
109 +
        socket_impl_->cancel_single_op(*this);
 
110 +
    else
 
111 +
        request_cancel();
 
112 +
}
 
113 +

 
114 +
// Completion handlers
 
115 +

 
116 +
inline void
 
117 +
select_datagram_op::operator()()
 
118 +
{
 
119 +
    complete_io_op(*this);
 
120 +
}
 
121 +

 
122 +
inline void
 
123 +
select_recv_from_op::operator()()
 
124 +
{
 
125 +
    complete_datagram_op(*this, this->source_out);
 
126 +
}
 
127 +

 
128 +
inline void
 
129 +
select_udp_connect_op::operator()()
 
130 +
{
 
131 +
    complete_connect_op(*this);
 
132 +
}
 
133 +

 
134 +
inline void
 
135 +
select_recv_op::operator()()
 
136 +
{
 
137 +
    complete_io_op(*this);
 
138 +
}
 
139 +

 
140 +
// Socket construction/destruction
 
141 +

 
142 +
inline select_udp_socket::select_udp_socket(select_udp_service& svc) noexcept
 
143 +
    : reactor_datagram_socket(svc)
 
144 +
{
 
145 +
}
 
146 +

 
147 +
inline select_udp_socket::~select_udp_socket() = default;
 
148 +

 
149 +
// Connectionless I/O
 
150 +

 
151 +
inline std::coroutine_handle<>
 
152 +
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 +
    auto result = do_send_to(h, ex, buf, dest, token, ec, bytes_out);
 
162 +
    if (result == std::noop_coroutine())
 
163 +
        svc_.scheduler().notify_reactor();
 
164 +
    return result;
 
165 +
}
 
166 +

 
167 +
inline std::coroutine_handle<>
 
168 +
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 +
    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 +
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 +
    auto result = do_connect(h, ex, ep, token, ec);
 
191 +
    if (result == std::noop_coroutine())
 
192 +
        svc_.scheduler().notify_reactor();
 
193 +
    return result;
 
194 +
}
 
195 +

 
196 +
inline std::coroutine_handle<>
 
197 +
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 +
    auto result = do_send(h, ex, buf, token, ec, bytes_out);
 
206 +
    if (result == std::noop_coroutine())
 
207 +
        svc_.scheduler().notify_reactor();
 
208 +
    return result;
 
209 +
}
 
210 +

 
211 +
inline std::coroutine_handle<>
 
212 +
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 +
    return do_recv(h, ex, buf, token, ec, bytes_out);
 
221 +
}
 
222 +

 
223 +
inline endpoint
 
224 +
select_udp_socket::remote_endpoint() const noexcept
 
225 +
{
 
226 +
    return reactor_datagram_socket::remote_endpoint();
 
227 +
}
 
228 +

 
229 +
inline void
 
230 +
select_udp_socket::cancel() noexcept
 
231 +
{
 
232 +
    do_cancel();
 
233 +
}
 
234 +

 
235 +
inline void
 
236 +
select_udp_socket::close_socket() noexcept
 
237 +
{
 
238 +
    do_close_socket();
 
239 +
}
 
240 +

 
241 +
inline std::error_code
 
242 +
select_udp_service::open_datagram_socket(
 
243 +
    udp_socket::implementation& impl, int family, int type, int protocol)
 
244 +
{
 
245 +
    auto* select_impl = static_cast<select_udp_socket*>(&impl);
 
246 +
    select_impl->close_socket();
 
247 +

 
248 +
    int fd = ::socket(family, type, protocol);
 
249 +
    if (fd < 0)
 
250 +
        return make_err(errno);
 
251 +

 
252 +
    if (family == AF_INET6)
 
253 +
    {
 
254 +
        int one = 1;
 
255 +
        ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
 
256 +
    }
 
257 +

 
258 +
    int flags = ::fcntl(fd, F_GETFL, 0);
 
259 +
    if (flags == -1)
 
260 +
    {
 
261 +
        int errn = errno;
 
262 +
        ::close(fd);
 
263 +
        return make_err(errn);
 
264 +
    }
 
265 +
    if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
 
266 +
    {
 
267 +
        int errn = errno;
 
268 +
        ::close(fd);
 
269 +
        return make_err(errn);
 
270 +
    }
 
271 +
    if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
 
272 +
    {
 
273 +
        int errn = errno;
 
274 +
        ::close(fd);
 
275 +
        return make_err(errn);
 
276 +
    }
 
277 +

 
278 +
    if (fd >= FD_SETSIZE)
 
279 +
    {
 
280 +
        ::close(fd);
 
281 +
        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 +
    select_impl->fd_ = fd;
 
292 +

 
293 +
    select_impl->desc_state_.fd = fd;
 
294 +
    {
 
295 +
        std::lock_guard lock(select_impl->desc_state_.mutex);
 
296 +
        select_impl->desc_state_.read_op    = nullptr;
 
297 +
        select_impl->desc_state_.write_op   = nullptr;
 
298 +
        select_impl->desc_state_.connect_op = nullptr;
 
299 +
    }
 
300 +
    scheduler().register_descriptor(fd, &select_impl->desc_state_);
 
301 +

 
302 +
    return {};
 
303 +
}
 
304 +

 
305 +
inline std::error_code
 
306 +
select_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
 
307 +
{
 
308 +
    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