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_EPOLL_EPOLL_UDP_SERVICE_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_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_EPOLL
15  
#if BOOST_COROSIO_HAS_EPOLL
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/epoll/epoll_udp_socket.hpp>
20  
#include <boost/corosio/native/detail/epoll/epoll_udp_socket.hpp>
21  
#include <boost/corosio/native/detail/epoll/epoll_scheduler.hpp>
21  
#include <boost/corosio/native/detail/epoll/epoll_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 <netinet/in.h>
30  
#include <netinet/in.h>
31  
#include <sys/epoll.h>
31  
#include <sys/epoll.h>
32  
#include <sys/socket.h>
32  
#include <sys/socket.h>
33  
#include <unistd.h>
33  
#include <unistd.h>
34  

34  

35  
namespace boost::corosio::detail {
35  
namespace boost::corosio::detail {
36  

36  

37  
/** epoll UDP service implementation.
37  
/** epoll UDP service implementation.
38  

38  

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

54  

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

63  

64  
// Cancellation for connectionless ops
64  
// Cancellation for connectionless ops
65  

65  

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

74  

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

83  

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

85  

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

94  

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

103  

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

112  

113  
// Completion handlers
113  
// Completion handlers
114  

114  

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

120  

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

126  

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

132  

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

138  

139  
// Socket construction/destruction
139  
// Socket construction/destruction
140  

140  

141  
inline epoll_udp_socket::epoll_udp_socket(epoll_udp_service& svc) noexcept
141  
inline epoll_udp_socket::epoll_udp_socket(epoll_udp_service& svc) noexcept
142  
    : reactor_datagram_socket(svc)
142  
    : reactor_datagram_socket(svc)
143  
{
143  
{
144  
}
144  
}
145  

145  

146  
inline epoll_udp_socket::~epoll_udp_socket() = default;
146  
inline epoll_udp_socket::~epoll_udp_socket() = default;
147  

147  

148  
// Connectionless I/O
148  
// Connectionless I/O
149  

149  

150  
inline std::coroutine_handle<>
150  
inline std::coroutine_handle<>
151  
epoll_udp_socket::send_to(
151  
epoll_udp_socket::send_to(
152  
    std::coroutine_handle<> h,
152  
    std::coroutine_handle<> h,
153  
    capy::executor_ref ex,
153  
    capy::executor_ref ex,
154  
    buffer_param buf,
154  
    buffer_param buf,
155  
    endpoint dest,
155  
    endpoint dest,
156  
    std::stop_token token,
156  
    std::stop_token token,
157  
    std::error_code* ec,
157  
    std::error_code* ec,
158  
    std::size_t* bytes_out)
158  
    std::size_t* bytes_out)
159  
{
159  
{
160  
    return do_send_to(h, ex, buf, dest, token, ec, bytes_out);
160  
    return do_send_to(h, ex, buf, dest, token, ec, bytes_out);
161  
}
161  
}
162  

162  

163  
inline std::coroutine_handle<>
163  
inline std::coroutine_handle<>
164  
epoll_udp_socket::recv_from(
164  
epoll_udp_socket::recv_from(
165  
    std::coroutine_handle<> h,
165  
    std::coroutine_handle<> h,
166  
    capy::executor_ref ex,
166  
    capy::executor_ref ex,
167  
    buffer_param buf,
167  
    buffer_param buf,
168  
    endpoint* source,
168  
    endpoint* source,
169  
    std::stop_token token,
169  
    std::stop_token token,
170  
    std::error_code* ec,
170  
    std::error_code* ec,
171  
    std::size_t* bytes_out)
171  
    std::size_t* bytes_out)
172  
{
172  
{
173  
    return do_recv_from(h, ex, buf, source, token, ec, bytes_out);
173  
    return do_recv_from(h, ex, buf, source, token, ec, bytes_out);
174  
}
174  
}
175  

175  

176  
// Connected-mode I/O
176  
// Connected-mode I/O
177  

177  

178  
inline std::coroutine_handle<>
178  
inline std::coroutine_handle<>
179  
epoll_udp_socket::connect(
179  
epoll_udp_socket::connect(
180  
    std::coroutine_handle<> h,
180  
    std::coroutine_handle<> h,
181  
    capy::executor_ref ex,
181  
    capy::executor_ref ex,
182  
    endpoint ep,
182  
    endpoint ep,
183  
    std::stop_token token,
183  
    std::stop_token token,
184  
    std::error_code* ec)
184  
    std::error_code* ec)
185  
{
185  
{
186  
    return do_connect(h, ex, ep, token, ec);
186  
    return do_connect(h, ex, ep, token, ec);
187  
}
187  
}
188  

188  

189  
inline std::coroutine_handle<>
189  
inline std::coroutine_handle<>
190  
epoll_udp_socket::send(
190  
epoll_udp_socket::send(
191  
    std::coroutine_handle<> h,
191  
    std::coroutine_handle<> h,
192  
    capy::executor_ref ex,
192  
    capy::executor_ref ex,
193  
    buffer_param buf,
193  
    buffer_param buf,
194  
    std::stop_token token,
194  
    std::stop_token token,
195  
    std::error_code* ec,
195  
    std::error_code* ec,
196  
    std::size_t* bytes_out)
196  
    std::size_t* bytes_out)
197  
{
197  
{
198  
    return do_send(h, ex, buf, token, ec, bytes_out);
198  
    return do_send(h, ex, buf, token, ec, bytes_out);
199  
}
199  
}
200  

200  

201  
inline std::coroutine_handle<>
201  
inline std::coroutine_handle<>
202  
epoll_udp_socket::recv(
202  
epoll_udp_socket::recv(
203  
    std::coroutine_handle<> h,
203  
    std::coroutine_handle<> h,
204  
    capy::executor_ref ex,
204  
    capy::executor_ref ex,
205  
    buffer_param buf,
205  
    buffer_param buf,
206  
    std::stop_token token,
206  
    std::stop_token token,
207  
    std::error_code* ec,
207  
    std::error_code* ec,
208  
    std::size_t* bytes_out)
208  
    std::size_t* bytes_out)
209  
{
209  
{
210  
    return do_recv(h, ex, buf, token, ec, bytes_out);
210  
    return do_recv(h, ex, buf, token, ec, bytes_out);
211  
}
211  
}
212  

212  

213  
inline endpoint
213  
inline endpoint
214  
epoll_udp_socket::remote_endpoint() const noexcept
214  
epoll_udp_socket::remote_endpoint() const noexcept
215  
{
215  
{
216  
    return reactor_datagram_socket::remote_endpoint();
216  
    return reactor_datagram_socket::remote_endpoint();
217  
}
217  
}
218  

218  

219  
inline void
219  
inline void
220  
epoll_udp_socket::cancel() noexcept
220  
epoll_udp_socket::cancel() noexcept
221  
{
221  
{
222  
    do_cancel();
222  
    do_cancel();
223  
}
223  
}
224  

224  

225  
inline void
225  
inline void
226  
epoll_udp_socket::close_socket() noexcept
226  
epoll_udp_socket::close_socket() noexcept
227  
{
227  
{
228  
    do_close_socket();
228  
    do_close_socket();
229  
}
229  
}
230  

230  

231  
inline std::error_code
231  
inline std::error_code
232  
epoll_udp_service::open_datagram_socket(
232  
epoll_udp_service::open_datagram_socket(
233  
    udp_socket::implementation& impl, int family, int type, int protocol)
233  
    udp_socket::implementation& impl, int family, int type, int protocol)
234  
{
234  
{
235  
    auto* epoll_impl = static_cast<epoll_udp_socket*>(&impl);
235  
    auto* epoll_impl = static_cast<epoll_udp_socket*>(&impl);
236  
    epoll_impl->close_socket();
236  
    epoll_impl->close_socket();
237  

237  

238  
    int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
238  
    int fd = ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
239  
    if (fd < 0)
239  
    if (fd < 0)
240  
        return make_err(errno);
240  
        return make_err(errno);
241  

241  

242  
    if (family == AF_INET6)
242  
    if (family == AF_INET6)
243  
    {
243  
    {
244  
        int one = 1;
244  
        int one = 1;
245  
        ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
245  
        ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
246  
    }
246  
    }
247  

247  

248  
    epoll_impl->fd_ = fd;
248  
    epoll_impl->fd_ = fd;
249  

249  

250  
    epoll_impl->desc_state_.fd = fd;
250  
    epoll_impl->desc_state_.fd = fd;
251  
    {
251  
    {
252  
        std::lock_guard lock(epoll_impl->desc_state_.mutex);
252  
        std::lock_guard lock(epoll_impl->desc_state_.mutex);
253  
        epoll_impl->desc_state_.read_op    = nullptr;
253  
        epoll_impl->desc_state_.read_op    = nullptr;
254  
        epoll_impl->desc_state_.write_op   = nullptr;
254  
        epoll_impl->desc_state_.write_op   = nullptr;
255  
        epoll_impl->desc_state_.connect_op = nullptr;
255  
        epoll_impl->desc_state_.connect_op = nullptr;
256  
    }
256  
    }
257  
    scheduler().register_descriptor(fd, &epoll_impl->desc_state_);
257  
    scheduler().register_descriptor(fd, &epoll_impl->desc_state_);
258  

258  

259  
    return {};
259  
    return {};
260  
}
260  
}
261  

261  

262  
inline std::error_code
262  
inline std::error_code
263  
epoll_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
263  
epoll_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
264  
{
264  
{
265  
    return static_cast<epoll_udp_socket*>(&impl)->do_bind(ep);
265  
    return static_cast<epoll_udp_socket*>(&impl)->do_bind(ep);
266  
}
266  
}
267  

267  

268  
} // namespace boost::corosio::detail
268  
} // namespace boost::corosio::detail
269  

269  

270  
#endif // BOOST_COROSIO_HAS_EPOLL
270  
#endif // BOOST_COROSIO_HAS_EPOLL
271  

271  

272  
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP
272  
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP