100.00% Lines (109/109) 100.00% Functions (29/29)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_UDP_SOCKET_HPP 11   #ifndef BOOST_COROSIO_UDP_SOCKET_HPP
12   #define BOOST_COROSIO_UDP_SOCKET_HPP 12   #define BOOST_COROSIO_UDP_SOCKET_HPP
13   13  
14   #include <boost/corosio/family.hpp> 14   #include <boost/corosio/family.hpp>
15   #include <boost/corosio/detail/config.hpp> 15   #include <boost/corosio/detail/config.hpp>
16   #include <boost/corosio/detail/platform.hpp> 16   #include <boost/corosio/detail/platform.hpp>
17   #include <boost/corosio/detail/except.hpp> 17   #include <boost/corosio/detail/except.hpp>
18   #include <boost/corosio/detail/native_handle.hpp> 18   #include <boost/corosio/detail/native_handle.hpp>
19   #include <boost/corosio/detail/op_base.hpp> 19   #include <boost/corosio/detail/op_base.hpp>
20   #include <boost/corosio/io/io_object.hpp> 20   #include <boost/corosio/io/io_object.hpp>
21   #include <boost/capy/io_result.hpp> 21   #include <boost/capy/io_result.hpp>
22   #include <boost/corosio/detail/buffer_param.hpp> 22   #include <boost/corosio/detail/buffer_param.hpp>
23   #include <boost/corosio/error.hpp> 23   #include <boost/corosio/error.hpp>
24   #include <boost/corosio/endpoint.hpp> 24   #include <boost/corosio/endpoint.hpp>
25   #include <boost/corosio/message_flags.hpp> 25   #include <boost/corosio/message_flags.hpp>
26   #include <boost/corosio/shutdown_type.hpp> 26   #include <boost/corosio/shutdown_type.hpp>
27   #include <boost/corosio/wait_type.hpp> 27   #include <boost/corosio/wait_type.hpp>
28   #include <boost/capy/ex/executor_ref.hpp> 28   #include <boost/capy/ex/executor_ref.hpp>
29   #include <boost/capy/ex/execution_context.hpp> 29   #include <boost/capy/ex/execution_context.hpp>
30   #include <boost/capy/ex/io_env.hpp> 30   #include <boost/capy/ex/io_env.hpp>
31   #include <boost/capy/concept/executor.hpp> 31   #include <boost/capy/concept/executor.hpp>
32   32  
33   #include <system_error> 33   #include <system_error>
34   34  
35   #include <concepts> 35   #include <concepts>
36   #include <coroutine> 36   #include <coroutine>
37   #include <cstddef> 37   #include <cstddef>
38   #include <stop_token> 38   #include <stop_token>
39   #include <type_traits> 39   #include <type_traits>
40   40  
41   namespace boost::corosio { 41   namespace boost::corosio {
42   42  
43   /** Sends and receives datagrams over UDP, from a coroutine. 43   /** Sends and receives datagrams over UDP, from a coroutine.
44   44  
45   This class provides asynchronous UDP datagram operations that 45   This class provides asynchronous UDP datagram operations that
46   return awaitable types. Each operation participates in the affine 46   return awaitable types. Each operation participates in the affine
47   awaitable protocol, ensuring coroutines resume on the correct 47   awaitable protocol, ensuring coroutines resume on the correct
48   executor. 48   executor.
49   49  
50   Supports two modes of operation: 50   Supports two modes of operation:
51   51  
52   **Connectionless mode**: each `send_to` specifies a destination 52   **Connectionless mode**: each `send_to` specifies a destination
53   endpoint, and each `recv_from` captures the source endpoint. 53   endpoint, and each `recv_from` captures the source endpoint.
54   The socket must be opened (and optionally bound) before I/O. 54   The socket must be opened (and optionally bound) before I/O.
55   55  
56   **Connected mode**: call `connect()` to set a default peer, 56   **Connected mode**: call `connect()` to set a default peer,
57   then use `send()`/`recv()` without endpoint arguments. 57   then use `send()`/`recv()` without endpoint arguments.
58   The kernel filters incoming datagrams to those from the 58   The kernel filters incoming datagrams to those from the
59   connected peer. 59   connected peer.
60   60  
61   @par Thread Safety 61   @par Thread Safety
62   Distinct objects: Safe.@n 62   Distinct objects: Safe.@n
63   Shared objects: Unsafe. A socket must not have concurrent 63   Shared objects: Unsafe. A socket must not have concurrent
64   operations of the same type (e.g., two simultaneous `recv_from`). 64   operations of the same type (e.g., two simultaneous `recv_from`).
65   One `send_to` and one `recv_from` may be in flight simultaneously. 65   One `send_to` and one `recv_from` may be in flight simultaneously.
66   66  
67   @par Example 67   @par Example
68   @par !example udp_socket 68   @par !example udp_socket
69   */ 69   */
70   class BOOST_COROSIO_DECL udp_socket : public io_object 70   class BOOST_COROSIO_DECL udp_socket : public io_object
71   { 71   {
72   public: 72   public:
73   /// The shutdown direction type used by this socket. 73   /// The shutdown direction type used by this socket.
74   using shutdown_type = corosio::shutdown_type; 74   using shutdown_type = corosio::shutdown_type;
75   using enum corosio::shutdown_type; 75   using enum corosio::shutdown_type;
76   76  
77   /** Define backend hooks for UDP socket operations. 77   /** Define backend hooks for UDP socket operations.
78   78  
79   Platform backends (epoll, kqueue, select) derive from 79   Platform backends (epoll, kqueue, select) derive from
80   this to implement datagram I/O and option management. 80   this to implement datagram I/O and option management.
81   */ 81   */
82   struct implementation : io_object::implementation 82   struct implementation : io_object::implementation
83   { 83   {
84   /** Initiate an asynchronous `send_to` operation. 84   /** Initiate an asynchronous `send_to` operation.
85   85  
86   @param h Coroutine handle to resume on completion. 86   @param h Coroutine handle to resume on completion.
87   @param ex Executor for dispatching the completion. 87   @param ex Executor for dispatching the completion.
88   @param buf The buffer data to send. 88   @param buf The buffer data to send.
89   @param dest The destination endpoint. 89   @param dest The destination endpoint.
90   @param flags Portable @ref message_flags bits (for example 90   @param flags Portable @ref message_flags bits (for example
91   `message_flags::do_not_route`). The backend translates 91   `message_flags::do_not_route`). The backend translates
92   these to native `MSG_*` constants. 92   these to native `MSG_*` constants.
93   @param token Stop token for cancellation. 93   @param token Stop token for cancellation.
94   @param ec Output error code. 94   @param ec Output error code.
95   @param bytes_out Output bytes transferred. 95   @param bytes_out Output bytes transferred.
96   96  
97   @return Coroutine handle to resume immediately. 97   @return Coroutine handle to resume immediately.
98   */ 98   */
99   virtual std::coroutine_handle<> send_to( 99   virtual std::coroutine_handle<> send_to(
100   std::coroutine_handle<> h, 100   std::coroutine_handle<> h,
101   capy::executor_ref ex, 101   capy::executor_ref ex,
102   buffer_param buf, 102   buffer_param buf,
103   endpoint dest, 103   endpoint dest,
104   int flags, 104   int flags,
105   std::stop_token token, 105   std::stop_token token,
106   std::error_code* ec, 106   std::error_code* ec,
107   std::size_t* bytes_out) = 0; 107   std::size_t* bytes_out) = 0;
108   108  
109   /** Initiate an asynchronous `recv_from` operation. 109   /** Initiate an asynchronous `recv_from` operation.
110   110  
111   @param h Coroutine handle to resume on completion. 111   @param h Coroutine handle to resume on completion.
112   @param ex Executor for dispatching the completion. 112   @param ex Executor for dispatching the completion.
113   @param buf The buffer to receive into. 113   @param buf The buffer to receive into.
114   @param source Output endpoint for the sender's address. 114   @param source Output endpoint for the sender's address.
115   @param flags Portable @ref message_flags bits (for example 115   @param flags Portable @ref message_flags bits (for example
116   `message_flags::peek`). The backend translates these to 116   `message_flags::peek`). The backend translates these to
117   native `MSG_*` constants. 117   native `MSG_*` constants.
118   @param token Stop token for cancellation. 118   @param token Stop token for cancellation.
119   @param ec Output error code. 119   @param ec Output error code.
120   @param bytes_out Output bytes transferred. 120   @param bytes_out Output bytes transferred.
121   121  
122   @return Coroutine handle to resume immediately. 122   @return Coroutine handle to resume immediately.
123   */ 123   */
124   virtual std::coroutine_handle<> recv_from( 124   virtual std::coroutine_handle<> recv_from(
125   std::coroutine_handle<> h, 125   std::coroutine_handle<> h,
126   capy::executor_ref ex, 126   capy::executor_ref ex,
127   buffer_param buf, 127   buffer_param buf,
128   endpoint* source, 128   endpoint* source,
129   int flags, 129   int flags,
130   std::stop_token token, 130   std::stop_token token,
131   std::error_code* ec, 131   std::error_code* ec,
132   std::size_t* bytes_out) = 0; 132   std::size_t* bytes_out) = 0;
133   133  
134   /// Return the platform socket descriptor. 134   /// Return the platform socket descriptor.
135   virtual native_handle_type native_handle() const noexcept = 0; 135   virtual native_handle_type native_handle() const noexcept = 0;
136   136  
137   /** Return the socket's address family. 137   /** Return the socket's address family.
138   138  
139   Socket options render for this family. 139   Socket options render for this family.
140   140  
141   @return The socket's address family. 141   @return The socket's address family.
142   */ 142   */
143   virtual corosio::family family() const noexcept = 0; 143   virtual corosio::family family() const noexcept = 0;
144   144  
145   /** Release ownership of the native socket handle. 145   /** Release ownership of the native socket handle.
146   146  
147   Deregisters the socket from the backend and cancels 147   Deregisters the socket from the backend and cancels
148   pending operations without closing the descriptor. The 148   pending operations without closing the descriptor. The
149   caller takes ownership. 149   caller takes ownership.
150   150  
151   @return The native handle. 151   @return The native handle.
152   */ 152   */
153   virtual native_handle_type release_socket() noexcept = 0; 153   virtual native_handle_type release_socket() noexcept = 0;
154   154  
155   /** Request cancellation of pending asynchronous operations. 155   /** Request cancellation of pending asynchronous operations.
156   156  
157   Operations still in flight complete with `operation_canceled`; 157   Operations still in flight complete with `operation_canceled`;
158   an operation whose result is already decided reports that 158   an operation whose result is already decided reports that
159   result. Check `ec == cond::canceled` for portable comparison. 159   result. Check `ec == cond::canceled` for portable comparison.
160   */ 160   */
161   virtual void cancel() noexcept = 0; 161   virtual void cancel() noexcept = 0;
162   162  
163   /** Shut down the socket in one or both directions. 163   /** Shut down the socket in one or both directions.
164   164  
165   @param what Which directions to disable. 165   @param what Which directions to disable.
166   166  
167   @return The error code, empty on success. 167   @return The error code, empty on success.
168   */ 168   */
169   virtual std::error_code shutdown(shutdown_type what) noexcept = 0; 169   virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
170   170  
171   /** Set a socket option. 171   /** Set a socket option.
172   172  
173   @param level The protocol level (e.g. `SOL_SOCKET`). 173   @param level The protocol level (e.g. `SOL_SOCKET`).
174   @param optname The option name. 174   @param optname The option name.
175   @param data Pointer to the option value. 175   @param data Pointer to the option value.
176   @param size Size of the option value in bytes. 176   @param size Size of the option value in bytes.
177   @return Error code on failure, empty on success. 177   @return Error code on failure, empty on success.
178   */ 178   */
179   virtual std::error_code set_option( 179   virtual std::error_code set_option(
180   int level, 180   int level,
181   int optname, 181   int optname,
182   void const* data, 182   void const* data,
183   std::size_t size) noexcept = 0; 183   std::size_t size) noexcept = 0;
184   184  
185   /** Get a socket option. 185   /** Get a socket option.
186   186  
187   @param level The protocol level (e.g. `SOL_SOCKET`). 187   @param level The protocol level (e.g. `SOL_SOCKET`).
188   @param optname The option name. 188   @param optname The option name.
189   @param data Pointer to receive the option value. 189   @param data Pointer to receive the option value.
190   @param size On entry, the size of the buffer. On exit, 190   @param size On entry, the size of the buffer. On exit,
191   the size of the option value. 191   the size of the option value.
192   @return Error code on failure, empty on success. 192   @return Error code on failure, empty on success.
193   */ 193   */
194   virtual std::error_code 194   virtual std::error_code
195   get_option(int level, int optname, void* data, std::size_t* size) 195   get_option(int level, int optname, void* data, std::size_t* size)
196   const noexcept = 0; 196   const noexcept = 0;
197   197  
198   /// Return the cached local endpoint. 198   /// Return the cached local endpoint.
199   virtual endpoint local_endpoint() const noexcept = 0; 199   virtual endpoint local_endpoint() const noexcept = 0;
200   200  
201   /// Return the cached remote endpoint (connected mode). 201   /// Return the cached remote endpoint (connected mode).
202   virtual endpoint remote_endpoint() const noexcept = 0; 202   virtual endpoint remote_endpoint() const noexcept = 0;
203   203  
204   /** Initiate an asynchronous connect to set the default peer. 204   /** Initiate an asynchronous connect to set the default peer.
205   205  
206   @param h Coroutine handle to resume on completion. 206   @param h Coroutine handle to resume on completion.
207   @param ex Executor for dispatching the completion. 207   @param ex Executor for dispatching the completion.
208   @param ep The remote endpoint to connect to. 208   @param ep The remote endpoint to connect to.
209   @param token Stop token for cancellation. 209   @param token Stop token for cancellation.
210   @param ec Output error code. 210   @param ec Output error code.
211   211  
212   @return Coroutine handle to resume immediately. 212   @return Coroutine handle to resume immediately.
213   */ 213   */
214   virtual std::coroutine_handle<> connect( 214   virtual std::coroutine_handle<> connect(
215   std::coroutine_handle<> h, 215   std::coroutine_handle<> h,
216   capy::executor_ref ex, 216   capy::executor_ref ex,
217   endpoint ep, 217   endpoint ep,
218   std::stop_token token, 218   std::stop_token token,
219   std::error_code* ec) = 0; 219   std::error_code* ec) = 0;
220   220  
221   /** Initiate an asynchronous connected send operation. 221   /** Initiate an asynchronous connected send operation.
222   222  
223   @param h Coroutine handle to resume on completion. 223   @param h Coroutine handle to resume on completion.
224   @param ex Executor for dispatching the completion. 224   @param ex Executor for dispatching the completion.
225   @param buf The buffer data to send. 225   @param buf The buffer data to send.
226   @param flags Portable @ref message_flags bits (for example 226   @param flags Portable @ref message_flags bits (for example
227   `message_flags::do_not_route`). The backend translates 227   `message_flags::do_not_route`). The backend translates
228   these to native `MSG_*` constants. 228   these to native `MSG_*` constants.
229   @param token Stop token for cancellation. 229   @param token Stop token for cancellation.
230   @param ec Output error code. 230   @param ec Output error code.
231   @param bytes_out Output bytes transferred. 231   @param bytes_out Output bytes transferred.
232   232  
233   @return Coroutine handle to resume immediately. 233   @return Coroutine handle to resume immediately.
234   */ 234   */
235   virtual std::coroutine_handle<> send( 235   virtual std::coroutine_handle<> send(
236   std::coroutine_handle<> h, 236   std::coroutine_handle<> h,
237   capy::executor_ref ex, 237   capy::executor_ref ex,
238   buffer_param buf, 238   buffer_param buf,
239   int flags, 239   int flags,
240   std::stop_token token, 240   std::stop_token token,
241   std::error_code* ec, 241   std::error_code* ec,
242   std::size_t* bytes_out) = 0; 242   std::size_t* bytes_out) = 0;
243   243  
244   /** Initiate an asynchronous connected `recv` operation. 244   /** Initiate an asynchronous connected `recv` operation.
245   245  
246   @param h Coroutine handle to resume on completion. 246   @param h Coroutine handle to resume on completion.
247   @param ex Executor for dispatching the completion. 247   @param ex Executor for dispatching the completion.
248   @param buf The buffer to receive into. 248   @param buf The buffer to receive into.
249   @param flags Portable @ref message_flags bits (for example 249   @param flags Portable @ref message_flags bits (for example
250   `message_flags::peek`). The backend translates these to 250   `message_flags::peek`). The backend translates these to
251   native `MSG_*` constants. 251   native `MSG_*` constants.
252   @param token Stop token for cancellation. 252   @param token Stop token for cancellation.
253   @param ec Output error code. 253   @param ec Output error code.
254   @param bytes_out Output bytes transferred. 254   @param bytes_out Output bytes transferred.
255   255  
256   @return Coroutine handle to resume immediately. 256   @return Coroutine handle to resume immediately.
257   */ 257   */
258   virtual std::coroutine_handle<> recv( 258   virtual std::coroutine_handle<> recv(
259   std::coroutine_handle<> h, 259   std::coroutine_handle<> h,
260   capy::executor_ref ex, 260   capy::executor_ref ex,
261   buffer_param buf, 261   buffer_param buf,
262   int flags, 262   int flags,
263   std::stop_token token, 263   std::stop_token token,
264   std::error_code* ec, 264   std::error_code* ec,
265   std::size_t* bytes_out) = 0; 265   std::size_t* bytes_out) = 0;
266   266  
267   /** Initiate an asynchronous wait for socket readiness. 267   /** Initiate an asynchronous wait for socket readiness.
268   268  
269   Completes when the socket becomes ready for the 269   Completes when the socket becomes ready for the
270   specified direction, or an error condition is 270   specified direction, or an error condition is
271   reported. No bytes are transferred. 271   reported. No bytes are transferred.
272   272  
273   @param h Coroutine handle to resume on completion. 273   @param h Coroutine handle to resume on completion.
274   @param ex Executor for dispatching the completion. 274   @param ex Executor for dispatching the completion.
275   @param w The direction to wait on. 275   @param w The direction to wait on.
276   @param token Stop token for cancellation. 276   @param token Stop token for cancellation.
277   @param ec Output error code. 277   @param ec Output error code.
278   278  
279   @return Coroutine handle to resume immediately. 279   @return Coroutine handle to resume immediately.
280   */ 280   */
281   virtual std::coroutine_handle<> wait( 281   virtual std::coroutine_handle<> wait(
282   std::coroutine_handle<> h, 282   std::coroutine_handle<> h,
283   capy::executor_ref ex, 283   capy::executor_ref ex,
284   wait_type w, 284   wait_type w,
285   std::stop_token token, 285   std::stop_token token,
286   std::error_code* ec) = 0; 286   std::error_code* ec) = 0;
287   }; 287   };
288   288  
289   /** Represent the awaitable returned by @ref send_to. 289   /** Represent the awaitable returned by @ref send_to.
290   290  
291   Captures the destination endpoint and buffer, then dispatches 291   Captures the destination endpoint and buffer, then dispatches
292   to the backend implementation on suspension. 292   to the backend implementation on suspension.
293   */ 293   */
294   struct send_to_awaitable : detail::bytes_op_base<send_to_awaitable> 294   struct send_to_awaitable : detail::bytes_op_base<send_to_awaitable>
295   { 295   {
296   private: 296   private:
297   friend udp_socket; 297   friend udp_socket;
298   298  
HITCBC 299   73 send_to_awaitable( 299   73 send_to_awaitable(
300   udp_socket& s, 300   udp_socket& s,
301   buffer_param buf, 301   buffer_param buf,
302   endpoint dest, 302   endpoint dest,
303   int flags = 0) noexcept 303   int flags = 0) noexcept
HITCBC 304   146 : s_(s) 304   146 : s_(s)
HITCBC 305   73 , buf_(buf) 305   73 , buf_(buf)
HITCBC 306   73 , dest_(dest) 306   73 , dest_(dest)
HITCBC 307   73 , flags_(flags) 307   73 , flags_(flags)
308   { 308   {
HITCBC 309   73 } 309   73 }
310   310  
311   friend detail::bytes_op_base<send_to_awaitable>; 311   friend detail::bytes_op_base<send_to_awaitable>;
312   312  
313   udp_socket& s_; 313   udp_socket& s_;
314   buffer_param buf_; 314   buffer_param buf_;
315   endpoint dest_; 315   endpoint dest_;
316   int flags_; 316   int flags_;
317   317  
318   std::coroutine_handle<> 318   std::coroutine_handle<>
HITCBC 319   69 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 319   69 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
320   { 320   {
HITCBC 321   138 return s_.get().send_to( 321   138 return s_.get().send_to(
HITCBC 322   138 h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_); 322   138 h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
323   } 323   }
324   }; 324   };
325   325  
326   /** Represent the awaitable returned by @ref recv_from. 326   /** Represent the awaitable returned by @ref recv_from.
327   327  
328   Captures the source endpoint reference and buffer, then 328   Captures the source endpoint reference and buffer, then
329   dispatches to the backend implementation on suspension. 329   dispatches to the backend implementation on suspension.
330   */ 330   */
331   struct recv_from_awaitable : detail::bytes_op_base<recv_from_awaitable> 331   struct recv_from_awaitable : detail::bytes_op_base<recv_from_awaitable>
332   { 332   {
333   private: 333   private:
334   friend udp_socket; 334   friend udp_socket;
335   335  
HITCBC 336   93 recv_from_awaitable( 336   93 recv_from_awaitable(
337   udp_socket& s, 337   udp_socket& s,
338   buffer_param buf, 338   buffer_param buf,
339   endpoint& source, 339   endpoint& source,
340   int flags = 0) noexcept 340   int flags = 0) noexcept
HITCBC 341   186 : s_(s) 341   186 : s_(s)
HITCBC 342   93 , buf_(buf) 342   93 , buf_(buf)
HITCBC 343   93 , source_(source) 343   93 , source_(source)
HITCBC 344   93 , flags_(flags) 344   93 , flags_(flags)
345   { 345   {
HITCBC 346   93 } 346   93 }
347   347  
348   friend detail::bytes_op_base<recv_from_awaitable>; 348   friend detail::bytes_op_base<recv_from_awaitable>;
349   349  
350   udp_socket& s_; 350   udp_socket& s_;
351   buffer_param buf_; 351   buffer_param buf_;
352   endpoint& source_; 352   endpoint& source_;
353   int flags_; 353   int flags_;
354   354  
355   std::coroutine_handle<> 355   std::coroutine_handle<>
HITCBC 356   87 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 356   87 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
357   { 357   {
HITCBC 358   174 return s_.get().recv_from( 358   174 return s_.get().recv_from(
HITCBC 359   174 h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_); 359   174 h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
360   } 360   }
361   }; 361   };
362   362  
363   /// Represent the awaitable returned by @ref connect. 363   /// Represent the awaitable returned by @ref connect.
364   struct connect_awaitable : detail::void_op_base<connect_awaitable> 364   struct connect_awaitable : detail::void_op_base<connect_awaitable>
365   { 365   {
366   private: 366   private:
367   friend udp_socket; 367   friend udp_socket;
368   368  
HITCBC 369   44 connect_awaitable(udp_socket& s, endpoint ep) noexcept 369   44 connect_awaitable(udp_socket& s, endpoint ep) noexcept
HITCBC 370   88 : s_(s) 370   88 : s_(s)
HITCBC 371   44 , endpoint_(ep) 371   44 , endpoint_(ep)
372   { 372   {
HITCBC 373   44 } 373   44 }
374   374  
375   friend detail::void_op_base<connect_awaitable>; 375   friend detail::void_op_base<connect_awaitable>;
376   376  
377   udp_socket& s_; 377   udp_socket& s_;
378   endpoint endpoint_; 378   endpoint endpoint_;
379   379  
380   std::coroutine_handle<> 380   std::coroutine_handle<>
HITCBC 381   42 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 381   42 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
382   { 382   {
HITCBC 383   42 return s_.get().connect(h, ex, endpoint_, token_, &ec_); 383   42 return s_.get().connect(h, ex, endpoint_, token_, &ec_);
384   } 384   }
385   }; 385   };
386   386  
387   /// Represent the awaitable returned by @ref wait. 387   /// Represent the awaitable returned by @ref wait.
388   struct wait_awaitable : detail::void_op_base<wait_awaitable> 388   struct wait_awaitable : detail::void_op_base<wait_awaitable>
389   { 389   {
390   private: 390   private:
391   friend udp_socket; 391   friend udp_socket;
392   392  
HITCBC 393   30 wait_awaitable(udp_socket& s, wait_type w) noexcept : s_(s), w_(w) {} 393   30 wait_awaitable(udp_socket& s, wait_type w) noexcept : s_(s), w_(w) {}
394   394  
395   friend detail::void_op_base<wait_awaitable>; 395   friend detail::void_op_base<wait_awaitable>;
396   396  
397   udp_socket& s_; 397   udp_socket& s_;
398   wait_type w_; 398   wait_type w_;
399   399  
400   std::coroutine_handle<> 400   std::coroutine_handle<>
HITCBC 401   28 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 401   28 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
402   { 402   {
HITCBC 403   28 return s_.get().wait(h, ex, w_, token_, &ec_); 403   28 return s_.get().wait(h, ex, w_, token_, &ec_);
404   } 404   }
405   }; 405   };
406   406  
407   /// Represent the awaitable returned by @ref send. 407   /// Represent the awaitable returned by @ref send.
408   struct send_awaitable : detail::bytes_op_base<send_awaitable> 408   struct send_awaitable : detail::bytes_op_base<send_awaitable>
409   { 409   {
410   private: 410   private:
411   friend udp_socket; 411   friend udp_socket;
412   412  
HITCBC 413   28 send_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept 413   28 send_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept
HITCBC 414   56 : s_(s) 414   56 : s_(s)
HITCBC 415   28 , buf_(buf) 415   28 , buf_(buf)
HITCBC 416   28 , flags_(flags) 416   28 , flags_(flags)
417   { 417   {
HITCBC 418   28 } 418   28 }
419   419  
420   friend detail::bytes_op_base<send_awaitable>; 420   friend detail::bytes_op_base<send_awaitable>;
421   421  
422   udp_socket& s_; 422   udp_socket& s_;
423   buffer_param buf_; 423   buffer_param buf_;
424   int flags_; 424   int flags_;
425   425  
426   std::coroutine_handle<> 426   std::coroutine_handle<>
HITCBC 427   24 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 427   24 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
428   { 428   {
HITCBC 429   24 return s_.get().send(h, ex, buf_, flags_, token_, &ec_, &bytes_); 429   24 return s_.get().send(h, ex, buf_, flags_, token_, &ec_, &bytes_);
430   } 430   }
431   }; 431   };
432   432  
433   /// Represent the awaitable returned by @ref recv. 433   /// Represent the awaitable returned by @ref recv.
434   struct recv_awaitable : detail::bytes_op_base<recv_awaitable> 434   struct recv_awaitable : detail::bytes_op_base<recv_awaitable>
435   { 435   {
436   private: 436   private:
437   friend udp_socket; 437   friend udp_socket;
438   438  
HITCBC 439   61 recv_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept 439   61 recv_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept
HITCBC 440   122 : s_(s) 440   122 : s_(s)
HITCBC 441   61 , buf_(buf) 441   61 , buf_(buf)
HITCBC 442   61 , flags_(flags) 442   61 , flags_(flags)
443   { 443   {
HITCBC 444   61 } 444   61 }
445   445  
446   friend detail::bytes_op_base<recv_awaitable>; 446   friend detail::bytes_op_base<recv_awaitable>;
447   447  
448   udp_socket& s_; 448   udp_socket& s_;
449   buffer_param buf_; 449   buffer_param buf_;
450   int flags_; 450   int flags_;
451   451  
452   std::coroutine_handle<> 452   std::coroutine_handle<>
HITCBC 453   57 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 453   57 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
454   { 454   {
HITCBC 455   57 return s_.get().recv(h, ex, buf_, flags_, token_, &ec_, &bytes_); 455   57 return s_.get().recv(h, ex, buf_, flags_, token_, &ec_, &bytes_);
456   } 456   }
457   }; 457   };
458   458  
459   public: 459   public:
460   /** Closes the socket if open, cancelling any pending operations. 460   /** Closes the socket if open, cancelling any pending operations.
461   */ 461   */
462   ~udp_socket() override; 462   ~udp_socket() override;
463   463  
464   /** Construct a socket from an execution context. 464   /** Construct a socket from an execution context.
465   465  
466   @param ctx The execution context that owns this socket. 466   @param ctx The execution context that owns this socket.
467   */ 467   */
468   explicit udp_socket(capy::execution_context& ctx); 468   explicit udp_socket(capy::execution_context& ctx);
469   469  
470   /** Construct a socket from an executor. 470   /** Construct a socket from an executor.
471   471  
472   The socket is associated with the executor's context. 472   The socket is associated with the executor's context.
473   473  
474   @param ex The executor whose context owns the socket. 474   @param ex The executor whose context owns the socket.
475   */ 475   */
476   template<class Ex> 476   template<class Ex>
477   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) && 477   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) &&
478   capy::Executor<Ex> 478   capy::Executor<Ex>
479   explicit udp_socket(Ex const& ex) : udp_socket(ex.context()) 479   explicit udp_socket(Ex const& ex) : udp_socket(ex.context())
480   { 480   {
481   } 481   }
482   482  
483   /** Transfers ownership of the socket resources. 483   /** Transfers ownership of the socket resources.
484   484  
485   @param other The socket to move from. 485   @param other The socket to move from.
486   */ 486   */
HITCBC 487   4 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {} 487   4 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {}
488   488  
489   /** Closes any existing socket and transfers ownership. 489   /** Closes any existing socket and transfers ownership.
490   490  
491   @param other The socket to move from. 491   @param other The socket to move from.
492   @return Reference to this socket. 492   @return Reference to this socket.
493   */ 493   */
HITCBC 494   2 udp_socket& operator=(udp_socket&& other) noexcept 494   2 udp_socket& operator=(udp_socket&& other) noexcept
495   { 495   {
HITCBC 496   2 if (this != &other) 496   2 if (this != &other)
497   { 497   {
HITCBC 498   2 close(); 498   2 close();
HITCBC 499   2 h_ = std::move(other.h_); 499   2 h_ = std::move(other.h_);
500   } 500   }
HITCBC 501   2 return *this; 501   2 return *this;
502   } 502   }
503   503  
504   /// Copy construction is disabled; the handle is uniquely owned. 504   /// Copy construction is disabled; the handle is uniquely owned.
505   udp_socket(udp_socket const&) = delete; 505   udp_socket(udp_socket const&) = delete;
506   /// Copy assignment is disabled; the handle is uniquely owned. 506   /// Copy assignment is disabled; the handle is uniquely owned.
507   udp_socket& operator=(udp_socket const&) = delete; 507   udp_socket& operator=(udp_socket const&) = delete;
508   508  
509   /** Open the socket. 509   /** Open the socket.
510   510  
511   Creates a UDP socket and associates it with the platform 511   Creates a UDP socket and associates it with the platform
512   reactor. 512   reactor.
513   513  
514   Failures such as descriptor exhaustion are normal runtime 514   Failures such as descriptor exhaustion are normal runtime
515   conditions and are reported through the returned error code. 515   conditions and are reported through the returned error code.
516   Opening an already-open socket is a no-op that reports 516   Opening an already-open socket is a no-op that reports
517   success. 517   success.
518   518  
519   @param f The address family (IPv4 or IPv6). Defaults to 519   @param f The address family (IPv4 or IPv6). Defaults to
520   `family::v4`. 520   `family::v4`.
521   521  
522   @return The error code, empty on success. 522   @return The error code, empty on success.
523   */ 523   */
524   [[nodiscard]] std::error_code open(family f = family::v4) noexcept; 524   [[nodiscard]] std::error_code open(family f = family::v4) noexcept;
525   525  
526   /** Close the socket. 526   /** Close the socket.
527   527  
528   Releases socket resources. Any pending operations complete 528   Releases socket resources. Any pending operations complete
529   with `errc::operation_canceled`. 529   with `errc::operation_canceled`.
530   */ 530   */
531   void close() noexcept; 531   void close() noexcept;
532   532  
533   /** Check if the socket is open. 533   /** Check if the socket is open.
534   534  
535   @return `true` if the socket is open and ready for operations. 535   @return `true` if the socket is open and ready for operations.
536   */ 536   */
HITCBC 537   1782 bool is_open() const noexcept 537   1782 bool is_open() const noexcept
538   { 538   {
539   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 539   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
540   return h_ && get().native_handle() != ~native_handle_type(0); 540   return h_ && get().native_handle() != ~native_handle_type(0);
541   #else 541   #else
HITCBC 542   1782 return h_ && get().native_handle() >= 0; 542   1782 return h_ && get().native_handle() >= 0;
543   #endif 543   #endif
544   } 544   }
545   545  
546   /** Bind the socket to a local endpoint. 546   /** Bind the socket to a local endpoint.
547   547  
548   Associates the socket with a local address and port. 548   Associates the socket with a local address and port.
549   Required before calling `recv_from`. 549   Required before calling `recv_from`.
550   550  
551   @param ep The local endpoint to bind to. 551   @param ep The local endpoint to bind to.
552   552  
553   @return Error code on failure, empty on success. 553   @return Error code on failure, empty on success.
554   554  
555   A closed socket reports `errc::bad_file_descriptor`. 555   A closed socket reports `errc::bad_file_descriptor`.
556   */ 556   */
557   [[nodiscard]] std::error_code bind(endpoint ep) noexcept; 557   [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
558   558  
559   /** Disable sends or receives on the socket. 559   /** Disable sends or receives on the socket.
560   560  
561   Failures such as an unconnected socket are normal runtime 561   Failures such as an unconnected socket are normal runtime
562   conditions and are reported through the returned error 562   conditions and are reported through the returned error
563   code. A closed socket reports `errc::bad_file_descriptor`. 563   code. A closed socket reports `errc::bad_file_descriptor`.
564   564  
565   @param what Determines which operations are no longer 565   @param what Determines which operations are no longer
566   allowed. 566   allowed.
567   567  
568   @return The error code, empty on success. 568   @return The error code, empty on success.
569   */ 569   */
570   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept; 570   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
571   571  
572   /** Cancel any pending asynchronous operations. 572   /** Cancel any pending asynchronous operations.
573   573  
574   Operations still in flight complete with 574   Operations still in flight complete with
575   `errc::operation_canceled`; an operation whose result is 575   `errc::operation_canceled`; an operation whose result is
576   already decided reports that result. Check 576   already decided reports that result. Check
577   `ec == cond::canceled` for portable comparison. 577   `ec == cond::canceled` for portable comparison.
578   */ 578   */
579   void cancel() noexcept; 579   void cancel() noexcept;
580   580  
581   /** Get the native socket handle. 581   /** Get the native socket handle.
582   582  
583   @return The native socket handle, or -1 if not open. 583   @return The native socket handle, or -1 if not open.
584   */ 584   */
585   native_handle_type native_handle() const noexcept; 585   native_handle_type native_handle() const noexcept;
586   586  
587   /** Assign an existing native socket to this object. 587   /** Assign an existing native socket to this object.
588   588  
589   Adopts a UDP socket created outside the library — received 589   Adopts a UDP socket created outside the library — received
590   from another process, inherited, or made natively — and 590   from another process, inherited, or made natively — and
591   registers it with the backend. The socket must be a datagram 591   registers it with the backend. The socket must be a datagram
592   socket in the `AF_INET` or `AF_INET6` family. Adoption never 592   socket in the `AF_INET` or `AF_INET6` family. Adoption never
593   alters the descriptor's flags or options: on POSIX the fd 593   alters the descriptor's flags or options: on POSIX the fd
594   must already be non-blocking, and on Windows the socket must 594   must already be non-blocking, and on Windows the socket must
595   be overlapped-capable. 595   be overlapped-capable.
596   596  
597   The object must be closed. To replace a held socket, `close()` 597   The object must be closed. To replace a held socket, `close()`
598   or `release()` it first. 598   or `release()` it first.
599   599  
600   @par Exception Safety 600   @par Exception Safety
601   Throws nothing. On failure the object is unchanged and the 601   Throws nothing. On failure the object is unchanged and the
602   caller retains ownership of `fd`. 602   caller retains ownership of `fd`.
603   603  
604   @param fd The native socket to adopt. On success the object 604   @param fd The native socket to adopt. On success the object
605   owns it and closes it. 605   owns it and closes it.
606   606  
607   @return `error::already_open` if this object is open. 607   @return `error::already_open` if this object is open.
608   Otherwise the error code, empty on success. Validation and 608   Otherwise the error code, empty on success. Validation and
609   registration failures are normal runtime conditions when 609   registration failures are normal runtime conditions when
610   adopting foreign descriptors. 610   adopting foreign descriptors.
611   */ 611   */
612   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 612   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
613   613  
614   /** Release ownership of the native socket handle. 614   /** Release ownership of the native socket handle.
615   615  
616   Deregisters the socket from the backend and cancels pending 616   Deregisters the socket from the backend and cancels pending
617   operations without closing the descriptor. The caller takes 617   operations without closing the descriptor. The caller takes
618   ownership of the returned handle. 618   ownership of the returned handle.
619   619  
620   @return The native handle. 620   @return The native handle.
621   621  
622   @throws std::system_error `errc::bad_file_descriptor` if the 622   @throws std::system_error `errc::bad_file_descriptor` if the
623   socket is not open. 623   socket is not open.
624   624  
625   @post is_open() == false 625   @post is_open() == false
626   */ 626   */
627   native_handle_type release(); 627   native_handle_type release();
628   628  
629   /** Set a socket option. 629   /** Set a socket option.
630   630  
631   @param opt The option to set. 631   @param opt The option to set.
632   632  
633   @throws std::system_error `errc::bad_file_descriptor` if the 633   @throws std::system_error `errc::bad_file_descriptor` if the
634   socket is not open; otherwise thrown on failure. 634   socket is not open; otherwise thrown on failure.
635   */ 635   */
636   template<class Option> 636   template<class Option>
HITCBC 637   97 void set_option(Option const& opt) 637   97 void set_option(Option const& opt)
638   { 638   {
HITCBC 639   97 if (!is_open()) 639   97 if (!is_open())
HITCBC 640   2 detail::throw_system_error( 640   2 detail::throw_system_error(
HITCBC 641   4 make_error_code(std::errc::bad_file_descriptor), 641   4 make_error_code(std::errc::bad_file_descriptor),
642   "udp_socket::set_option"); 642   "udp_socket::set_option");
HITCBC 643   95 auto const fam = get().family(); 643   95 auto const fam = get().family();
HITCBC 644   95 std::error_code ec = get().set_option( 644   95 std::error_code ec = get().set_option(
645   opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam)); 645   opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
HITCBC 646   95 if (ec) 646   95 if (ec)
HITCBC 647   6 detail::throw_system_error(ec, "udp_socket::set_option"); 647   6 detail::throw_system_error(ec, "udp_socket::set_option");
HITCBC 648   89 } 648   89 }
649   649  
650   /** Get a socket option. 650   /** Get a socket option.
651   651  
652   @return The current option value. 652   @return The current option value.
653   653  
654   @throws std::system_error `errc::bad_file_descriptor` if the 654   @throws std::system_error `errc::bad_file_descriptor` if the
655   socket is not open; otherwise thrown on failure. 655   socket is not open; otherwise thrown on failure.
656   */ 656   */
657   template<class Option> 657   template<class Option>
HITCBC 658   63 Option get_option() const 658   63 Option get_option() const
659   { 659   {
HITCBC 660   63 if (!is_open()) 660   63 if (!is_open())
HITCBC 661   2 detail::throw_system_error( 661   2 detail::throw_system_error(
HITCBC 662   4 make_error_code(std::errc::bad_file_descriptor), 662   4 make_error_code(std::errc::bad_file_descriptor),
663   "udp_socket::get_option"); 663   "udp_socket::get_option");
HITCBC 664   61 Option opt{}; 664   61 Option opt{};
HITCBC 665   61 auto const fam = get().family(); 665   61 auto const fam = get().family();
HITCBC 666   61 std::size_t sz = opt.size(fam); 666   61 std::size_t sz = opt.size(fam);
667   std::error_code ec = 667   std::error_code ec =
HITCBC 668   61 get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz); 668   61 get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz);
HITCBC 669   61 if (ec) 669   61 if (ec)
HITCBC 670   2 detail::throw_system_error(ec, "udp_socket::get_option"); 670   2 detail::throw_system_error(ec, "udp_socket::get_option");
HITCBC 671   59 opt.resize(fam, sz); 671   59 opt.resize(fam, sz);
HITCBC 672   59 return opt; 672   59 return opt;
673   } 673   }
674   674  
675   /** Get the local endpoint of the socket. 675   /** Get the local endpoint of the socket.
676   676  
677   @return The local endpoint, or a default endpoint if not bound. 677   @return The local endpoint, or a default endpoint if not bound.
678   */ 678   */
679   endpoint local_endpoint() const noexcept; 679   endpoint local_endpoint() const noexcept;
680   680  
681   /** Send a datagram to the specified destination. 681   /** Send a datagram to the specified destination.
682   682  
683   @param buf The buffer containing data to send. 683   @param buf The buffer containing data to send.
684   @param dest The destination endpoint. 684   @param dest The destination endpoint.
685   @param flags Message flags (e.g. message_flags::do_not_route). 685   @param flags Message flags (e.g. message_flags::do_not_route).
686   686  
687   @return An awaitable that completes with 687   @return An awaitable that completes with
688   `io_result<std::size_t>`. 688   `io_result<std::size_t>`.
689   689  
690   A closed socket reports `errc::bad_file_descriptor`. 690   A closed socket reports `errc::bad_file_descriptor`.
691   */ 691   */
692   template<capy::ConstBufferSequence Buffers> 692   template<capy::ConstBufferSequence Buffers>
693   [[nodiscard]] auto 693   [[nodiscard]] auto
HITCBC 694   73 send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags) 694   73 send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags)
695   { 695   {
HITCBC 696   73 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags)); 696   73 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
HITCBC 697   73 if (!is_open()) 697   73 if (!is_open())
HITCBC 698   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 698   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 699   73 return aw; 699   73 return aw;
700   } 700   }
701   701  
702   /// @overload 702   /// @overload
703   template<capy::ConstBufferSequence Buffers> 703   template<capy::ConstBufferSequence Buffers>
HITCBC 704   73 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest) 704   73 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest)
705   { 705   {
HITCBC 706   73 return send_to(buf, dest, corosio::message_flags::none); 706   73 return send_to(buf, dest, corosio::message_flags::none);
707   } 707   }
708   708  
709   /** Receive a datagram and capture the sender's endpoint. 709   /** Receive a datagram and capture the sender's endpoint.
710   710  
711   @param buf The buffer to receive data into. 711   @param buf The buffer to receive data into.
712   @param source Reference to an endpoint that receives 712   @param source Reference to an endpoint that receives
713   the sender's address on successful completion. 713   the sender's address on successful completion.
714   @param flags Message flags (e.g. message_flags::peek). 714   @param flags Message flags (e.g. message_flags::peek).
715   715  
716   @return An awaitable that completes with 716   @return An awaitable that completes with
717   `io_result<std::size_t>`. 717   `io_result<std::size_t>`.
718   718  
719   A closed socket reports `errc::bad_file_descriptor`. 719   A closed socket reports `errc::bad_file_descriptor`.
720   */ 720   */
721   template<capy::MutableBufferSequence Buffers> 721   template<capy::MutableBufferSequence Buffers>
HITCBC 722   93 [[nodiscard]] auto recv_from( 722   93 [[nodiscard]] auto recv_from(
723   Buffers const& buf, endpoint& source, corosio::message_flags flags) 723   Buffers const& buf, endpoint& source, corosio::message_flags flags)
724   { 724   {
HITCBC 725   93 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags)); 725   93 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
HITCBC 726   93 if (!is_open()) 726   93 if (!is_open())
HITCBC 727   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 727   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 728   93 return aw; 728   93 return aw;
729   } 729   }
730   730  
731   /// @overload 731   /// @overload
732   template<capy::MutableBufferSequence Buffers> 732   template<capy::MutableBufferSequence Buffers>
HITCBC 733   90 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source) 733   90 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source)
734   { 734   {
HITCBC 735   90 return recv_from(buf, source, corosio::message_flags::none); 735   90 return recv_from(buf, source, corosio::message_flags::none);
736   } 736   }
737   737  
738   /** Initiate an asynchronous connect to set the default peer. 738   /** Initiate an asynchronous connect to set the default peer.
739   739  
740   If the socket is not already open, it is opened automatically 740   If the socket is not already open, it is opened automatically
741   using the address family of @p ep. 741   using the address family of @p ep.
742   742  
743   @param ep The remote endpoint to connect to. 743   @param ep The remote endpoint to connect to.
744   744  
745   @return An awaitable that completes with `io_result<>`. 745   @return An awaitable that completes with `io_result<>`.
746   746  
747   If the socket needs to be opened and the open fails, the 747   If the socket needs to be opened and the open fails, the
748   awaitable completes immediately with that error. 748   awaitable completes immediately with that error.
749   */ 749   */
HITCBC 750   44 [[nodiscard]] auto connect(endpoint ep) 750   44 [[nodiscard]] auto connect(endpoint ep)
751   { 751   {
HITCBC 752   44 connect_awaitable aw(*this, ep); 752   44 connect_awaitable aw(*this, ep);
HITCBC 753   44 if (!is_open()) 753   44 if (!is_open())
HITCBC 754   10 aw.ec_ = open(ep.address().family()); 754   10 aw.ec_ = open(ep.address().family());
HITCBC 755   44 return aw; 755   44 return aw;
756   } 756   }
757   757  
758   /** Wait for the socket to become ready in a given direction. 758   /** Wait for the socket to become ready in a given direction.
759   759  
760   Suspends until the socket is ready for the requested 760   Suspends until the socket is ready for the requested
761   direction, or an error condition is reported. No bytes 761   direction, or an error condition is reported. No bytes
762   are transferred. 762   are transferred.
763   763  
764   The operation supports cancellation via `std::stop_token`. 764   The operation supports cancellation via `std::stop_token`.
765   765  
766   @param w The wait direction (read, write, or error). 766   @param w The wait direction (read, write, or error).
767   767  
768   @return An awaitable that completes with `io_result<>`. 768   @return An awaitable that completes with `io_result<>`.
769   769  
770   A closed socket completes with `errc::bad_file_descriptor`. 770   A closed socket completes with `errc::bad_file_descriptor`.
771   771  
772   @pre This socket must outlive the returned awaitable. 772   @pre This socket must outlive the returned awaitable.
773   */ 773   */
HITCBC 774   30 [[nodiscard]] auto wait(wait_type w) 774   30 [[nodiscard]] auto wait(wait_type w)
775   { 775   {
HITCBC 776   30 return wait_awaitable(*this, w); 776   30 return wait_awaitable(*this, w);
777   } 777   }
778   778  
779   /** Send a datagram to the connected peer. 779   /** Send a datagram to the connected peer.
780   780  
781   @param buf The buffer containing data to send. 781   @param buf The buffer containing data to send.
782   @param flags Message flags. 782   @param flags Message flags.
783   783  
784   @return An awaitable that completes with 784   @return An awaitable that completes with
785   `io_result<std::size_t>`. 785   `io_result<std::size_t>`.
786   786  
787   A closed socket reports `errc::bad_file_descriptor`. 787   A closed socket reports `errc::bad_file_descriptor`.
788   */ 788   */
789   template<capy::ConstBufferSequence Buffers> 789   template<capy::ConstBufferSequence Buffers>
HITCBC 790   28 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags) 790   28 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
791   { 791   {
HITCBC 792   28 send_awaitable aw(*this, buf, static_cast<int>(flags)); 792   28 send_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 793   28 if (!is_open()) 793   28 if (!is_open())
HITCBC 794   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 794   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 795   28 return aw; 795   28 return aw;
796   } 796   }
797   797  
798   /// @overload 798   /// @overload
799   template<capy::ConstBufferSequence Buffers> 799   template<capy::ConstBufferSequence Buffers>
HITCBC 800   28 [[nodiscard]] auto send(Buffers const& buf) 800   28 [[nodiscard]] auto send(Buffers const& buf)
801   { 801   {
HITCBC 802   28 return send(buf, corosio::message_flags::none); 802   28 return send(buf, corosio::message_flags::none);
803   } 803   }
804   804  
805   /** Receive a datagram from the connected peer. 805   /** Receive a datagram from the connected peer.
806   806  
807   @param buf The buffer to receive data into. 807   @param buf The buffer to receive data into.
808   @param flags Message flags (e.g. message_flags::peek). 808   @param flags Message flags (e.g. message_flags::peek).
809   809  
810   @return An awaitable that completes with 810   @return An awaitable that completes with
811   `io_result<std::size_t>`. 811   `io_result<std::size_t>`.
812   812  
813   A closed socket reports `errc::bad_file_descriptor`. 813   A closed socket reports `errc::bad_file_descriptor`.
814   */ 814   */
815   template<capy::MutableBufferSequence Buffers> 815   template<capy::MutableBufferSequence Buffers>
HITCBC 816   61 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags) 816   61 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
817   { 817   {
HITCBC 818   61 recv_awaitable aw(*this, buf, static_cast<int>(flags)); 818   61 recv_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 819   61 if (!is_open()) 819   61 if (!is_open())
HITCBC 820   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 820   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 821   61 return aw; 821   61 return aw;
822   } 822   }
823   823  
824   /// @overload 824   /// @overload
825   template<capy::MutableBufferSequence Buffers> 825   template<capy::MutableBufferSequence Buffers>
HITCBC 826   59 [[nodiscard]] auto recv(Buffers const& buf) 826   59 [[nodiscard]] auto recv(Buffers const& buf)
827   { 827   {
HITCBC 828   59 return recv(buf, corosio::message_flags::none); 828   59 return recv(buf, corosio::message_flags::none);
829   } 829   }
830   830  
831   /** Get the remote endpoint of the socket. 831   /** Get the remote endpoint of the socket.
832   832  
833   Returns the address and port of the connected peer. 833   Returns the address and port of the connected peer.
834   834  
835   @return The remote endpoint, or a default endpoint if 835   @return The remote endpoint, or a default endpoint if
836   not connected. 836   not connected.
837   */ 837   */
838   endpoint remote_endpoint() const noexcept; 838   endpoint remote_endpoint() const noexcept;
839   839  
840   protected: 840   protected:
841   /// Construct from a pre-built handle (for native_udp_socket). 841   /// Construct from a pre-built handle (for native_udp_socket).
HITCBC 842   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h)) 842   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
843   { 843   {
HITCBC 844   42 } 844   42 }
845   845  
846   private: 846   private:
847   /// Open the socket for the given protocol triple. 847   /// Open the socket for the given protocol triple.
848   [[nodiscard]] std::error_code 848   [[nodiscard]] std::error_code
849   open_for_family(int family, int type, int protocol) noexcept; 849   open_for_family(int family, int type, int protocol) noexcept;
850   850  
HITCBC 851   2605 inline implementation& get() const noexcept 851   2605 inline implementation& get() const noexcept
852   { 852   {
HITCBC 853   2605 return *static_cast<implementation*>(h_.get()); 853   2605 return *static_cast<implementation*>(h_.get());
854   } 854   }
855   }; 855   };
856   856  
857   } // namespace boost::corosio 857   } // namespace boost::corosio
858   858  
859   #endif // BOOST_COROSIO_UDP_SOCKET_HPP 859   #endif // BOOST_COROSIO_UDP_SOCKET_HPP