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