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