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