1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
12  

12  

13  
#include <boost/corosio/detail/platform.hpp>
13  
#include <boost/corosio/detail/platform.hpp>
14  

14  

15  
#if BOOST_COROSIO_HAS_SELECT
15  
#if BOOST_COROSIO_HAS_SELECT
16  

16  

17  
#include <boost/corosio/detail/config.hpp>
17  
#include <boost/corosio/detail/config.hpp>
18  
#include <boost/corosio/detail/udp_service.hpp>
18  
#include <boost/corosio/detail/udp_service.hpp>
19  

19  

20  
#include <boost/corosio/native/detail/select/select_udp_socket.hpp>
20  
#include <boost/corosio/native/detail/select/select_udp_socket.hpp>
21  
#include <boost/corosio/native/detail/select/select_scheduler.hpp>
21  
#include <boost/corosio/native/detail/select/select_scheduler.hpp>
22  
#include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
22  
#include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
23  

23  

24  
#include <boost/corosio/native/detail/reactor/reactor_op_complete.hpp>
24  
#include <boost/corosio/native/detail/reactor/reactor_op_complete.hpp>
25  

25  

26  
#include <coroutine>
26  
#include <coroutine>
27  
#include <mutex>
27  
#include <mutex>
28  

28  

29  
#include <errno.h>
29  
#include <errno.h>
30  
#include <fcntl.h>
30  
#include <fcntl.h>
31  
#include <netinet/in.h>
31  
#include <netinet/in.h>
32  
#include <sys/select.h>
32  
#include <sys/select.h>
33  
#include <sys/socket.h>
33  
#include <sys/socket.h>
34  
#include <unistd.h>
34  
#include <unistd.h>
35  

35  

36  
namespace boost::corosio::detail {
36  
namespace boost::corosio::detail {
37  

37  

38  
/** select UDP service implementation.
38  
/** select UDP service implementation.
39  

39  

40  
    Inherits from udp_service to enable runtime polymorphism.
40  
    Inherits from udp_service to enable runtime polymorphism.
41  
    Uses key_type = udp_service for service lookup.
41  
    Uses key_type = udp_service for service lookup.
42  
*/
42  
*/
43  
class BOOST_COROSIO_DECL select_udp_service final
43  
class BOOST_COROSIO_DECL select_udp_service final
44  
    : public reactor_socket_service<
44  
    : public reactor_socket_service<
45  
          select_udp_service,
45  
          select_udp_service,
46  
          udp_service,
46  
          udp_service,
47  
          select_scheduler,
47  
          select_scheduler,
48  
          select_udp_socket>
48  
          select_udp_socket>
49  
{
49  
{
50  
public:
50  
public:
51  
    explicit select_udp_service(capy::execution_context& ctx)
51  
    explicit select_udp_service(capy::execution_context& ctx)
52  
        : reactor_socket_service(ctx)
52  
        : reactor_socket_service(ctx)
53  
    {
53  
    {
54  
    }
54  
    }
55  

55  

56  
    std::error_code open_datagram_socket(
56  
    std::error_code open_datagram_socket(
57  
        udp_socket::implementation& impl,
57  
        udp_socket::implementation& impl,
58  
        int family,
58  
        int family,
59  
        int type,
59  
        int type,
60  
        int protocol) override;
60  
        int protocol) override;
61  
    std::error_code
61  
    std::error_code
62  
    bind_datagram(udp_socket::implementation& impl, endpoint ep) override;
62  
    bind_datagram(udp_socket::implementation& impl, endpoint ep) override;
63  
};
63  
};
64  

64  

65  
// Cancellation for connectionless ops
65  
// Cancellation for connectionless ops
66  

66  

67  
inline void
67  
inline void
68  
select_send_to_op::cancel() noexcept
68  
select_send_to_op::cancel() noexcept
69  
{
69  
{
70  
    if (socket_impl_)
70  
    if (socket_impl_)
71  
        socket_impl_->cancel_single_op(*this);
71  
        socket_impl_->cancel_single_op(*this);
72  
    else
72  
    else
73  
        request_cancel();
73  
        request_cancel();
74  
}
74  
}
75  

75  

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

84  

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

86  

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

95  

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

104  

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

113  

114  
// Completion handlers
114  
// Completion handlers
115  

115  

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

121  

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

127  

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

133  

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

139  

140  
// Socket construction/destruction
140  
// Socket construction/destruction
141  

141  

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

146  

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

148  

149  
// Connectionless I/O
149  
// Connectionless I/O
150  

150  

151  
inline std::coroutine_handle<>
151  
inline std::coroutine_handle<>
152  
select_udp_socket::send_to(
152  
select_udp_socket::send_to(
153  
    std::coroutine_handle<> h,
153  
    std::coroutine_handle<> h,
154  
    capy::executor_ref ex,
154  
    capy::executor_ref ex,
155  
    buffer_param buf,
155  
    buffer_param buf,
156  
    endpoint dest,
156  
    endpoint dest,
157  
    std::stop_token token,
157  
    std::stop_token token,
158  
    std::error_code* ec,
158  
    std::error_code* ec,
159  
    std::size_t* bytes_out)
159  
    std::size_t* bytes_out)
160  
{
160  
{
161  
    auto result = do_send_to(h, ex, buf, dest, token, ec, bytes_out);
161  
    auto result = do_send_to(h, ex, buf, dest, token, ec, bytes_out);
162  
    if (result == std::noop_coroutine())
162  
    if (result == std::noop_coroutine())
163  
        svc_.scheduler().notify_reactor();
163  
        svc_.scheduler().notify_reactor();
164  
    return result;
164  
    return result;
165  
}
165  
}
166  

166  

167  
inline std::coroutine_handle<>
167  
inline std::coroutine_handle<>
168  
select_udp_socket::recv_from(
168  
select_udp_socket::recv_from(
169  
    std::coroutine_handle<> h,
169  
    std::coroutine_handle<> h,
170  
    capy::executor_ref ex,
170  
    capy::executor_ref ex,
171  
    buffer_param buf,
171  
    buffer_param buf,
172  
    endpoint* source,
172  
    endpoint* source,
173  
    std::stop_token token,
173  
    std::stop_token token,
174  
    std::error_code* ec,
174  
    std::error_code* ec,
175  
    std::size_t* bytes_out)
175  
    std::size_t* bytes_out)
176  
{
176  
{
177  
    return do_recv_from(h, ex, buf, source, token, ec, bytes_out);
177  
    return do_recv_from(h, ex, buf, source, token, ec, bytes_out);
178  
}
178  
}
179  

179  

180  
// Connected-mode I/O
180  
// Connected-mode I/O
181  

181  

182  
inline std::coroutine_handle<>
182  
inline std::coroutine_handle<>
183  
select_udp_socket::connect(
183  
select_udp_socket::connect(
184  
    std::coroutine_handle<> h,
184  
    std::coroutine_handle<> h,
185  
    capy::executor_ref ex,
185  
    capy::executor_ref ex,
186  
    endpoint ep,
186  
    endpoint ep,
187  
    std::stop_token token,
187  
    std::stop_token token,
188  
    std::error_code* ec)
188  
    std::error_code* ec)
189  
{
189  
{
190  
    auto result = do_connect(h, ex, ep, token, ec);
190  
    auto result = do_connect(h, ex, ep, token, ec);
191  
    if (result == std::noop_coroutine())
191  
    if (result == std::noop_coroutine())
192  
        svc_.scheduler().notify_reactor();
192  
        svc_.scheduler().notify_reactor();
193  
    return result;
193  
    return result;
194  
}
194  
}
195  

195  

196  
inline std::coroutine_handle<>
196  
inline std::coroutine_handle<>
197  
select_udp_socket::send(
197  
select_udp_socket::send(
198  
    std::coroutine_handle<> h,
198  
    std::coroutine_handle<> h,
199  
    capy::executor_ref ex,
199  
    capy::executor_ref ex,
200  
    buffer_param buf,
200  
    buffer_param buf,
201  
    std::stop_token token,
201  
    std::stop_token token,
202  
    std::error_code* ec,
202  
    std::error_code* ec,
203  
    std::size_t* bytes_out)
203  
    std::size_t* bytes_out)
204  
{
204  
{
205  
    auto result = do_send(h, ex, buf, token, ec, bytes_out);
205  
    auto result = do_send(h, ex, buf, token, ec, bytes_out);
206  
    if (result == std::noop_coroutine())
206  
    if (result == std::noop_coroutine())
207  
        svc_.scheduler().notify_reactor();
207  
        svc_.scheduler().notify_reactor();
208  
    return result;
208  
    return result;
209  
}
209  
}
210  

210  

211  
inline std::coroutine_handle<>
211  
inline std::coroutine_handle<>
212  
select_udp_socket::recv(
212  
select_udp_socket::recv(
213  
    std::coroutine_handle<> h,
213  
    std::coroutine_handle<> h,
214  
    capy::executor_ref ex,
214  
    capy::executor_ref ex,
215  
    buffer_param buf,
215  
    buffer_param buf,
216  
    std::stop_token token,
216  
    std::stop_token token,
217  
    std::error_code* ec,
217  
    std::error_code* ec,
218  
    std::size_t* bytes_out)
218  
    std::size_t* bytes_out)
219  
{
219  
{
220  
    return do_recv(h, ex, buf, token, ec, bytes_out);
220  
    return do_recv(h, ex, buf, token, ec, bytes_out);
221  
}
221  
}
222  

222  

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

228  

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

234  

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

240  

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

247  

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

251  

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

257  

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

277  

278  
    if (fd >= FD_SETSIZE)
278  
    if (fd >= FD_SETSIZE)
279  
    {
279  
    {
280  
        ::close(fd);
280  
        ::close(fd);
281  
        return make_err(EMFILE);
281  
        return make_err(EMFILE);
282  
    }
282  
    }
283  

283  

284  
#ifdef SO_NOSIGPIPE
284  
#ifdef SO_NOSIGPIPE
285  
    {
285  
    {
286  
        int one = 1;
286  
        int one = 1;
287  
        ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
287  
        ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
288  
    }
288  
    }
289  
#endif
289  
#endif
290  

290  

291  
    select_impl->fd_ = fd;
291  
    select_impl->fd_ = fd;
292  

292  

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

301  

302  
    return {};
302  
    return {};
303  
}
303  
}
304  

304  

305  
inline std::error_code
305  
inline std::error_code
306  
select_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
306  
select_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
307  
{
307  
{
308  
    return static_cast<select_udp_socket*>(&impl)->do_bind(ep);
308  
    return static_cast<select_udp_socket*>(&impl)->do_bind(ep);
309  
}
309  
}
310  

310  

311  
} // namespace boost::corosio::detail
311  
} // namespace boost::corosio::detail
312  

312  

313  
#endif // BOOST_COROSIO_HAS_SELECT
313  
#endif // BOOST_COROSIO_HAS_SELECT
314  

314  

315  
#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP
315  
#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_UDP_SERVICE_HPP