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