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