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