TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
12 : #define BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
13 :
14 : #include <boost/corosio/udp_socket.hpp>
15 : #include <boost/corosio/backend.hpp>
16 : #include <boost/corosio/detail/op_base.hpp>
17 :
18 : #ifndef BOOST_COROSIO_MRDOCS
19 : #if BOOST_COROSIO_HAS_EPOLL
20 : #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
21 : #endif
22 :
23 : #if BOOST_COROSIO_HAS_SELECT
24 : #include <boost/corosio/native/detail/select/select_types.hpp>
25 : #endif
26 :
27 : #if BOOST_COROSIO_HAS_KQUEUE
28 : #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
29 : #endif
30 :
31 : #if BOOST_COROSIO_HAS_URING
32 : #include <boost/corosio/native/detail/uring/uring_types.hpp>
33 : #endif
34 :
35 : #if BOOST_COROSIO_HAS_IOCP
36 : #include <boost/corosio/native/detail/iocp/win_udp_service.hpp>
37 : #endif
38 : #endif // !BOOST_COROSIO_MRDOCS
39 :
40 : namespace boost::corosio {
41 :
42 : /** An asynchronous UDP socket with devirtualized I/O operations.
43 :
44 : This class template inherits from @ref udp_socket and shadows
45 : the async operations (`send_to`, `recv_from`, `connect`, `send`,
46 : `recv`) with versions that call the backend implementation
47 : directly, allowing the compiler to inline through the entire
48 : call chain.
49 :
50 : Non-async operations (`open`, `close`, `cancel`, `bind`,
51 : socket options) remain unchanged and dispatch through the
52 : compiled library.
53 :
54 : A `native_udp_socket` IS-A `udp_socket` and can be passed to
55 : any function expecting `udp_socket&`, in which case virtual
56 : dispatch is used transparently.
57 :
58 : @tparam Backend A backend tag value (e.g., `epoll`)
59 : whose type provides the concrete implementation types.
60 :
61 : @par Thread Safety
62 : Same as @ref udp_socket.
63 :
64 : @par Example
65 : @par !example native_udp_socket
66 :
67 : @see udp_socket, epoll_t
68 : */
69 : template<auto Backend>
70 : class native_udp_socket : public udp_socket
71 : {
72 : using backend_type = decltype(Backend);
73 : using impl_type = typename backend_type::udp_socket_type;
74 : using service_type = typename backend_type::udp_service_type;
75 :
76 HIT 28 : impl_type& get_impl() noexcept
77 : {
78 28 : return *static_cast<impl_type*>(h_.get());
79 : }
80 :
81 : template<class ConstBufferSequence>
82 : struct native_send_to_awaitable
83 : : detail::bytes_op_base<native_send_to_awaitable<ConstBufferSequence>>
84 : {
85 : native_udp_socket& self_;
86 : ConstBufferSequence buffers_;
87 : endpoint dest_;
88 : int flags_;
89 :
90 8 : native_send_to_awaitable(
91 : native_udp_socket& self,
92 : ConstBufferSequence buffers,
93 : endpoint dest,
94 : int flags) noexcept
95 8 : : self_(self)
96 8 : , buffers_(std::move(buffers))
97 8 : , dest_(dest)
98 8 : , flags_(flags)
99 : {
100 8 : }
101 :
102 : std::coroutine_handle<>
103 4 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
104 : {
105 12 : return self_.get_impl().send_to(
106 4 : h, ex, buffers_, dest_, flags_, this->token_, &this->ec_,
107 8 : &this->bytes_);
108 : }
109 : };
110 :
111 : template<class MutableBufferSequence>
112 : struct native_recv_from_awaitable
113 : : detail::bytes_op_base<
114 : native_recv_from_awaitable<MutableBufferSequence>>
115 : {
116 : native_udp_socket& self_;
117 : MutableBufferSequence buffers_;
118 : endpoint& source_;
119 : int flags_;
120 :
121 12 : native_recv_from_awaitable(
122 : native_udp_socket& self,
123 : MutableBufferSequence buffers,
124 : endpoint& source,
125 : int flags) noexcept
126 12 : : self_(self)
127 12 : , buffers_(std::move(buffers))
128 12 : , source_(source)
129 12 : , flags_(flags)
130 : {
131 12 : }
132 :
133 : std::coroutine_handle<>
134 8 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
135 : {
136 24 : return self_.get_impl().recv_from(
137 8 : h, ex, buffers_, &source_, flags_, this->token_, &this->ec_,
138 16 : &this->bytes_);
139 : }
140 : };
141 :
142 : struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable>
143 : {
144 : native_udp_socket& self_;
145 : wait_type w_;
146 :
147 4 : native_wait_awaitable(native_udp_socket& self, wait_type w) noexcept
148 4 : : self_(self)
149 4 : , w_(w)
150 : {
151 4 : }
152 :
153 : std::coroutine_handle<>
154 2 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
155 : {
156 2 : return self_.get_impl().wait(h, ex, w_, this->token_, &this->ec_);
157 : }
158 : };
159 :
160 : struct native_connect_awaitable
161 : : detail::void_op_base<native_connect_awaitable>
162 : {
163 : native_udp_socket& self_;
164 : endpoint endpoint_;
165 :
166 10 : native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept
167 10 : : self_(self)
168 10 : , endpoint_(ep)
169 : {
170 10 : }
171 :
172 : std::coroutine_handle<>
173 8 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
174 : {
175 24 : return self_.get_impl().connect(
176 24 : h, ex, endpoint_, this->token_, &this->ec_);
177 : }
178 : };
179 :
180 : template<class ConstBufferSequence>
181 : struct native_send_awaitable
182 : : detail::bytes_op_base<native_send_awaitable<ConstBufferSequence>>
183 : {
184 : native_udp_socket& self_;
185 : ConstBufferSequence buffers_;
186 : int flags_;
187 :
188 8 : native_send_awaitable(
189 : native_udp_socket& self,
190 : ConstBufferSequence buffers,
191 : int flags) noexcept
192 8 : : self_(self)
193 8 : , buffers_(std::move(buffers))
194 8 : , flags_(flags)
195 : {
196 8 : }
197 :
198 : std::coroutine_handle<>
199 4 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
200 : {
201 12 : return self_.get_impl().send(
202 4 : h, ex, buffers_, flags_, this->token_, &this->ec_,
203 8 : &this->bytes_);
204 : }
205 : };
206 :
207 : template<class MutableBufferSequence>
208 : struct native_recv_awaitable
209 : : detail::bytes_op_base<native_recv_awaitable<MutableBufferSequence>>
210 : {
211 : native_udp_socket& self_;
212 : MutableBufferSequence buffers_;
213 : int flags_;
214 :
215 6 : native_recv_awaitable(
216 : native_udp_socket& self,
217 : MutableBufferSequence buffers,
218 : int flags) noexcept
219 6 : : self_(self)
220 6 : , buffers_(std::move(buffers))
221 6 : , flags_(flags)
222 : {
223 6 : }
224 :
225 : std::coroutine_handle<>
226 2 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
227 : {
228 6 : return self_.get_impl().recv(
229 2 : h, ex, buffers_, flags_, this->token_, &this->ec_,
230 4 : &this->bytes_);
231 : }
232 : };
233 :
234 : public:
235 : /** Construct a native UDP socket from an execution context.
236 :
237 : @param ctx The execution context that will own this socket.
238 : */
239 42 : explicit native_udp_socket(capy::execution_context& ctx)
240 42 : : udp_socket(create_handle<service_type>(ctx))
241 : {
242 42 : }
243 :
244 : /** Construct a native UDP socket from an executor.
245 :
246 : @param ex The executor whose context will own the socket.
247 : */
248 : template<class Ex>
249 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) &&
250 : capy::Executor<Ex>
251 : explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context())
252 : {
253 : }
254 :
255 : /// Move construct.
256 2 : native_udp_socket(native_udp_socket&&) noexcept = default;
257 :
258 : /// Move assign.
259 : native_udp_socket& operator=(native_udp_socket&&) noexcept = default;
260 :
261 : native_udp_socket(native_udp_socket const&) = delete;
262 : native_udp_socket& operator=(native_udp_socket const&) = delete;
263 :
264 : /** Send a datagram to the specified destination.
265 :
266 : Calls the backend implementation directly, bypassing virtual
267 : dispatch. Otherwise identical to @ref udp_socket::send_to.
268 :
269 : @param buffers The buffer sequence containing data to send.
270 : @param dest The destination endpoint.
271 : @param flags Message flags.
272 :
273 : @return An awaitable yielding `(error_code, std::size_t)`.
274 :
275 : A closed socket reports `errc::bad_file_descriptor`.
276 : */
277 : template<capy::ConstBufferSequence CB>
278 : [[nodiscard]] auto
279 8 : send_to(CB const& buffers, endpoint dest, corosio::message_flags flags)
280 : {
281 8 : native_send_to_awaitable<CB> aw(
282 : *this, buffers, dest, static_cast<int>(flags));
283 8 : if (!is_open())
284 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
285 8 : return aw;
286 : }
287 :
288 : /// @overload
289 : template<capy::ConstBufferSequence CB>
290 8 : [[nodiscard]] auto send_to(CB const& buffers, endpoint dest)
291 : {
292 8 : return send_to(buffers, dest, corosio::message_flags::none);
293 : }
294 :
295 : /** Receive a datagram and capture the sender's endpoint.
296 :
297 : Calls the backend implementation directly, bypassing virtual
298 : dispatch. Otherwise identical to @ref udp_socket::recv_from.
299 :
300 : @param buffers The buffer sequence to receive data into.
301 : @param source Reference to an endpoint that will be set to
302 : the sender's address on successful completion.
303 : @param flags Message flags (e.g. message_flags::peek).
304 :
305 : @return An awaitable yielding `(error_code, std::size_t)`.
306 :
307 : A closed socket reports `errc::bad_file_descriptor`.
308 : */
309 : template<capy::MutableBufferSequence MB>
310 : [[nodiscard]] auto
311 12 : recv_from(MB const& buffers, endpoint& source, corosio::message_flags flags)
312 : {
313 12 : native_recv_from_awaitable<MB> aw(
314 : *this, buffers, source, static_cast<int>(flags));
315 12 : if (!is_open())
316 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
317 12 : return aw;
318 : }
319 :
320 : /// @overload
321 : template<capy::MutableBufferSequence MB>
322 12 : [[nodiscard]] auto recv_from(MB const& buffers, endpoint& source)
323 : {
324 12 : return recv_from(buffers, source, corosio::message_flags::none);
325 : }
326 :
327 : /** Asynchronously connect to set the default peer.
328 :
329 : Calls the backend implementation directly, bypassing virtual
330 : dispatch. Otherwise identical to @ref udp_socket::connect.
331 :
332 : If the socket is not already open, it is opened automatically
333 : using the address family of @p ep.
334 :
335 : @param ep The remote endpoint to connect to.
336 :
337 : @return An awaitable yielding `io_result<>`.
338 :
339 : If the socket needs to be opened and the open fails, the
340 : awaitable completes immediately with that error.
341 : */
342 10 : [[nodiscard]] auto connect(endpoint ep)
343 : {
344 10 : native_connect_awaitable aw(*this, ep);
345 10 : if (!is_open())
346 4 : aw.ec_ = open(ep.address().family());
347 10 : return aw;
348 : }
349 :
350 : /** Send a datagram to the connected peer.
351 :
352 : Calls the backend implementation directly, bypassing virtual
353 : dispatch. Otherwise identical to @ref udp_socket::send.
354 :
355 : @param buffers The buffer sequence containing data to send.
356 : @param flags Message flags.
357 :
358 : @return An awaitable yielding `(error_code, std::size_t)`.
359 :
360 : A closed socket reports `errc::bad_file_descriptor`.
361 : */
362 : template<capy::ConstBufferSequence CB>
363 8 : [[nodiscard]] auto send(CB const& buffers, corosio::message_flags flags)
364 : {
365 8 : native_send_awaitable<CB> aw(*this, buffers, static_cast<int>(flags));
366 8 : if (!is_open())
367 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
368 8 : return aw;
369 : }
370 :
371 : /// @overload
372 : template<capy::ConstBufferSequence CB>
373 8 : [[nodiscard]] auto send(CB const& buffers)
374 : {
375 8 : return send(buffers, corosio::message_flags::none);
376 : }
377 :
378 : /** Receive a datagram from the connected peer.
379 :
380 : Calls the backend implementation directly, bypassing virtual
381 : dispatch. Otherwise identical to @ref udp_socket::recv.
382 :
383 : @param buffers The buffer sequence to receive data into.
384 : @param flags Message flags (e.g. message_flags::peek).
385 :
386 : @return An awaitable yielding `(error_code, std::size_t)`.
387 :
388 : A closed socket reports `errc::bad_file_descriptor`.
389 : */
390 : template<capy::MutableBufferSequence MB>
391 6 : [[nodiscard]] auto recv(MB const& buffers, corosio::message_flags flags)
392 : {
393 6 : native_recv_awaitable<MB> aw(*this, buffers, static_cast<int>(flags));
394 6 : if (!is_open())
395 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
396 6 : return aw;
397 : }
398 :
399 : /// @overload
400 : template<capy::MutableBufferSequence MB>
401 6 : [[nodiscard]] auto recv(MB const& buffers)
402 : {
403 6 : return recv(buffers, corosio::message_flags::none);
404 : }
405 :
406 : /** Asynchronously wait for the socket to be ready.
407 :
408 : Calls the backend implementation directly, bypassing virtual
409 : dispatch. Otherwise identical to @ref udp_socket::wait.
410 :
411 : @param w The wait direction (read, write, or error).
412 :
413 : @return An awaitable yielding `io_result<>`.
414 : */
415 4 : [[nodiscard]] auto wait(wait_type w)
416 : {
417 4 : return native_wait_awaitable(*this, w);
418 : }
419 : };
420 :
421 : } // namespace boost::corosio
422 :
423 : #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
|