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(capy::continuation& cont, capy::executor_ref ex) const
103 : {
104 12 : return self_.get_impl().send_to(
105 4 : cont, 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(capy::continuation& cont, capy::executor_ref ex) const
134 : {
135 24 : return self_.get_impl().recv_from(
136 8 : cont, 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(capy::continuation& cont, capy::executor_ref ex) const
154 : {
155 4 : return self_.get_impl().wait(
156 4 : cont, 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(capy::continuation& cont, capy::executor_ref ex) const
174 : {
175 24 : return self_.get_impl().connect(
176 24 : cont, 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(capy::continuation& cont, capy::executor_ref ex) const
200 : {
201 12 : return self_.get_impl().send(
202 4 : cont, 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(capy::continuation& cont, capy::executor_ref ex) const
227 : {
228 6 : return self_.get_impl().recv(
229 2 : cont, 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 owns this socket.
238 : */
239 42 : explicit native_udp_socket(capy::execution_context& ctx)
240 42 : : udp_socket(handle(ctx, ctx.use_service<service_type>()))
241 : {
242 42 : }
243 :
244 : /** Construct a native UDP socket from an executor.
245 :
246 : @param ex The executor whose context owns the socket.
247 :
248 : @tparam Ex A type satisfying @ref capy::Executor. Must not
249 : be `native_udp_socket` itself (disables implicit
250 : conversion from move).
251 : */
252 : template<class Ex>
253 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) &&
254 : capy::Executor<Ex>
255 : explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context())
256 : {
257 : }
258 :
259 : /** Move construct.
260 :
261 : @param other The socket to move from.
262 :
263 : @pre No awaitables returned by @p other's methods exist.
264 : @pre The execution context associated with @p other must
265 : outlive this socket.
266 : */
267 2 : native_udp_socket(native_udp_socket&&) noexcept = default;
268 :
269 : /** Move assign.
270 :
271 : @param other The socket to move from.
272 :
273 : @pre No awaitables returned by either `*this` or @p other's
274 : methods exist.
275 : @pre The execution context associated with @p other must
276 : outlive this socket.
277 : */
278 : native_udp_socket& operator=(native_udp_socket&&) noexcept = default;
279 :
280 : /// Copy construction is disabled; the handle is uniquely owned.
281 : native_udp_socket(native_udp_socket const&) = delete;
282 : /// Copy assignment is disabled; the handle is uniquely owned.
283 : native_udp_socket& operator=(native_udp_socket const&) = delete;
284 :
285 : /** Send a datagram to the specified destination.
286 :
287 : Calls the backend implementation directly, bypassing virtual
288 : dispatch. Otherwise identical to @ref udp_socket::send_to.
289 :
290 : @param buffers The buffer sequence containing data to send.
291 : @param dest The destination endpoint.
292 : @param flags Message flags.
293 :
294 : @return An awaitable yielding `(error_code, std::size_t)`.
295 :
296 : A closed socket reports `errc::bad_file_descriptor`.
297 : */
298 : template<capy::ConstBufferSequence CB>
299 : [[nodiscard]] auto
300 8 : send_to(CB const& buffers, endpoint dest, corosio::message_flags flags)
301 : {
302 8 : native_send_to_awaitable<CB> aw(
303 : *this, buffers, dest, static_cast<int>(flags));
304 8 : if (!is_open())
305 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
306 8 : return aw;
307 : }
308 :
309 : /// @overload
310 : template<capy::ConstBufferSequence CB>
311 8 : [[nodiscard]] auto send_to(CB const& buffers, endpoint dest)
312 : {
313 8 : return send_to(buffers, dest, corosio::message_flags::none);
314 : }
315 :
316 : /** Receive a datagram and capture the sender's endpoint.
317 :
318 : Calls the backend implementation directly, bypassing virtual
319 : dispatch. Otherwise identical to @ref udp_socket::recv_from.
320 :
321 : @param buffers The buffer sequence to receive data into.
322 : @param source Reference to an endpoint that receives
323 : the sender's address on successful completion.
324 : @param flags Message flags (e.g. message_flags::peek).
325 :
326 : @return An awaitable yielding `(error_code, std::size_t)`.
327 :
328 : A closed socket reports `errc::bad_file_descriptor`.
329 : */
330 : template<capy::MutableBufferSequence MB>
331 : [[nodiscard]] auto
332 12 : recv_from(MB const& buffers, endpoint& source, corosio::message_flags flags)
333 : {
334 12 : native_recv_from_awaitable<MB> aw(
335 : *this, buffers, source, static_cast<int>(flags));
336 12 : if (!is_open())
337 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
338 12 : return aw;
339 : }
340 :
341 : /// @overload
342 : template<capy::MutableBufferSequence MB>
343 12 : [[nodiscard]] auto recv_from(MB const& buffers, endpoint& source)
344 : {
345 12 : return recv_from(buffers, source, corosio::message_flags::none);
346 : }
347 :
348 : /** Asynchronously connect to set the default peer.
349 :
350 : Calls the backend implementation directly, bypassing virtual
351 : dispatch. Otherwise identical to @ref udp_socket::connect.
352 :
353 : If the socket is not already open, it is opened automatically
354 : using the address family of @p ep.
355 :
356 : @param ep The remote endpoint to connect to.
357 :
358 : @return An awaitable yielding `results<>`.
359 :
360 : If the socket needs to be opened and the open fails, the
361 : awaitable completes immediately with that error.
362 : */
363 10 : [[nodiscard]] auto connect(endpoint ep)
364 : {
365 10 : native_connect_awaitable aw(*this, ep);
366 10 : if (!is_open())
367 4 : aw.ec_ = open(ep.address().family());
368 10 : return aw;
369 : }
370 :
371 : /** Send a datagram to the connected peer.
372 :
373 : Calls the backend implementation directly, bypassing virtual
374 : dispatch. Otherwise identical to @ref udp_socket::send.
375 :
376 : @param buffers The buffer sequence containing data to send.
377 : @param flags Message flags.
378 :
379 : @return An awaitable yielding `(error_code, std::size_t)`.
380 :
381 : A closed socket reports `errc::bad_file_descriptor`.
382 : */
383 : template<capy::ConstBufferSequence CB>
384 8 : [[nodiscard]] auto send(CB const& buffers, corosio::message_flags flags)
385 : {
386 8 : native_send_awaitable<CB> aw(*this, buffers, static_cast<int>(flags));
387 8 : if (!is_open())
388 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
389 8 : return aw;
390 : }
391 :
392 : /// @overload
393 : template<capy::ConstBufferSequence CB>
394 8 : [[nodiscard]] auto send(CB const& buffers)
395 : {
396 8 : return send(buffers, corosio::message_flags::none);
397 : }
398 :
399 : /** Receive a datagram from the connected peer.
400 :
401 : Calls the backend implementation directly, bypassing virtual
402 : dispatch. Otherwise identical to @ref udp_socket::recv.
403 :
404 : @param buffers The buffer sequence to receive data into.
405 : @param flags Message flags (e.g. message_flags::peek).
406 :
407 : @return An awaitable yielding `(error_code, std::size_t)`.
408 :
409 : A closed socket reports `errc::bad_file_descriptor`.
410 : */
411 : template<capy::MutableBufferSequence MB>
412 6 : [[nodiscard]] auto recv(MB const& buffers, corosio::message_flags flags)
413 : {
414 6 : native_recv_awaitable<MB> aw(*this, buffers, static_cast<int>(flags));
415 6 : if (!is_open())
416 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
417 6 : return aw;
418 : }
419 :
420 : /// @overload
421 : template<capy::MutableBufferSequence MB>
422 6 : [[nodiscard]] auto recv(MB const& buffers)
423 : {
424 6 : return recv(buffers, corosio::message_flags::none);
425 : }
426 :
427 : /** Asynchronously wait for the socket to be ready.
428 :
429 : Calls the backend implementation directly, bypassing virtual
430 : dispatch. Otherwise identical to @ref udp_socket::wait.
431 :
432 : @param w The wait direction (read, write, or error).
433 :
434 : @return An awaitable yielding `results<>`.
435 : */
436 4 : [[nodiscard]] auto wait(wait_type w)
437 : {
438 4 : return native_wait_awaitable(*this, w);
439 : }
440 : };
441 :
442 : } // namespace boost::corosio
443 :
444 : #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
|