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