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