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_EPOLL_EPOLL_UDP_SERVICE_HPP
 
11 +
#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP
 
12 +

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

 
15 +
#if BOOST_COROSIO_HAS_EPOLL
 
16 +

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

 
20 +
#include <boost/corosio/native/detail/epoll/epoll_udp_socket.hpp>
 
21 +
#include <boost/corosio/native/detail/epoll/epoll_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 <netinet/in.h>
 
31 +
#include <sys/epoll.h>
 
32 +
#include <sys/socket.h>
 
33 +
#include <unistd.h>
 
34 +

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

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

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

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

 
64 +
// Cancellation for connectionless ops
 
65 +

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

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

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

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

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

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

 
113 +
// Completion handlers
 
114 +

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

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

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

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

 
139 +
// Socket construction/destruction
 
140 +

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

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

 
148 +
// Connectionless I/O
 
149 +

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

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

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

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

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

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

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

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

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

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

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

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

 
248 +
    epoll_impl->fd_ = fd;
 
249 +

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

 
259 +
    return {};
 
260 +
}
 
261 +

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

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

 
270 +
#endif // BOOST_COROSIO_HAS_EPOLL
 
271 +

 
272 +
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_UDP_SERVICE_HPP