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