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