100.00% Lines (104/104) 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/native_handle.hpp> 17   #include <boost/corosio/detail/native_handle.hpp>
18   #include <boost/corosio/detail/op_base.hpp> 18   #include <boost/corosio/detail/op_base.hpp>
19   #include <boost/corosio/io/io_object.hpp> 19   #include <boost/corosio/io/io_object.hpp>
20   #include <boost/capy/results.hpp> 20   #include <boost/capy/results.hpp>
21   #include <boost/corosio/detail/buffer_param.hpp> 21   #include <boost/corosio/detail/buffer_param.hpp>
22   #include <boost/corosio/error.hpp> 22   #include <boost/corosio/error.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/await_context.hpp> 29   #include <boost/capy/ex/await_context.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 <boost/capy/continuation.hpp> 35   #include <boost/capy/continuation.hpp>
36   36  
37   #include <coroutine> 37   #include <coroutine>
38   #include <cstddef> 38   #include <cstddef>
39   #include <stop_token> 39   #include <stop_token>
40   #include <type_traits> 40   #include <type_traits>
41   #include <utility> 41   #include <utility>
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   detail::buffer_param buf, 105   detail::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   detail::buffer_param buf, 131   detail::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   detail::buffer_param buf, 244   detail::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   detail::buffer_param buf, 268   detail::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   detail::buffer_param buf, 309   detail::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   detail::buffer_param buf_; 322   detail::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   detail::buffer_param buf, 346   detail::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   detail::buffer_param buf_; 359   detail::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( 421   28 send_awaitable(
422   udp_socket& s, detail::buffer_param buf, int flags = 0) noexcept 422   udp_socket& s, detail::buffer_param buf, int flags = 0) noexcept
HITCBC 423   56 : s_(s) 423   56 : s_(s)
HITCBC 424   28 , buf_(buf) 424   28 , buf_(buf)
HITCBC 425   28 , flags_(flags) 425   28 , flags_(flags)
426   { 426   {
HITCBC 427   28 } 427   28 }
428   428  
429   friend detail::bytes_op_base<send_awaitable>; 429   friend detail::bytes_op_base<send_awaitable>;
430   430  
431   udp_socket& s_; 431   udp_socket& s_;
432   detail::buffer_param buf_; 432   detail::buffer_param buf_;
433   int flags_; 433   int flags_;
434   434  
435   std::coroutine_handle<> 435   std::coroutine_handle<>
HITCBC 436   24 dispatch(capy::continuation& cont, capy::executor_ref ex) const 436   24 dispatch(capy::continuation& cont, capy::executor_ref ex) const
437   { 437   {
HITCBC 438   24 return s_.get().send(cont, ex, buf_, flags_, token_, &ec_, &bytes_); 438   24 return s_.get().send(cont, ex, buf_, flags_, token_, &ec_, &bytes_);
439   } 439   }
440   }; 440   };
441   441  
442   /// Represent the awaitable returned by @ref recv. 442   /// Represent the awaitable returned by @ref recv.
443   struct recv_awaitable : detail::bytes_op_base<recv_awaitable> 443   struct recv_awaitable : detail::bytes_op_base<recv_awaitable>
444   { 444   {
445   private: 445   private:
446   friend udp_socket; 446   friend udp_socket;
447   447  
HITCBC 448   72 recv_awaitable( 448   72 recv_awaitable(
449   udp_socket& s, detail::buffer_param buf, int flags = 0) noexcept 449   udp_socket& s, detail::buffer_param buf, int flags = 0) noexcept
HITCBC 450   144 : s_(s) 450   144 : s_(s)
HITCBC 451   72 , buf_(buf) 451   72 , buf_(buf)
HITCBC 452   72 , flags_(flags) 452   72 , flags_(flags)
453   { 453   {
HITCBC 454   72 } 454   72 }
455   455  
456   friend detail::bytes_op_base<recv_awaitable>; 456   friend detail::bytes_op_base<recv_awaitable>;
457   457  
458   udp_socket& s_; 458   udp_socket& s_;
459   detail::buffer_param buf_; 459   detail::buffer_param buf_;
460   int flags_; 460   int flags_;
461   461  
462   std::coroutine_handle<> 462   std::coroutine_handle<>
HITCBC 463   68 dispatch(capy::continuation& cont, capy::executor_ref ex) const 463   68 dispatch(capy::continuation& cont, capy::executor_ref ex) const
464   { 464   {
HITCBC 465   68 return s_.get().recv(cont, ex, buf_, flags_, token_, &ec_, &bytes_); 465   68 return s_.get().recv(cont, ex, buf_, flags_, token_, &ec_, &bytes_);
466   } 466   }
467   }; 467   };
468   468  
469   public: 469   public:
470   /** Closes the socket if open, cancelling any pending operations. 470   /** Closes the socket if open, cancelling any pending operations.
471   */ 471   */
472   ~udp_socket() override; 472   ~udp_socket() override;
473   473  
474   /** Construct a socket from an execution context. 474   /** Construct a socket from an execution context.
475   475  
476   @param ctx The execution context that owns this socket. 476   @param ctx The execution context that owns this socket.
477   */ 477   */
478   explicit udp_socket(capy::execution_context& ctx); 478   explicit udp_socket(capy::execution_context& ctx);
479   479  
480   /** Construct a socket from an executor. 480   /** Construct a socket from an executor.
481   481  
482   The socket is associated with the executor's context. 482   The socket is associated with the executor's context.
483   483  
484   @param ex The executor whose context owns the socket. 484   @param ex The executor whose context owns the socket.
485   */ 485   */
486   template<class Ex> 486   template<class Ex>
487   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) && 487   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) &&
488   capy::Executor<Ex> 488   capy::Executor<Ex>
489   explicit udp_socket(Ex const& ex) : udp_socket(ex.context()) 489   explicit udp_socket(Ex const& ex) : udp_socket(ex.context())
490   { 490   {
491   } 491   }
492   492  
493   /** Transfers ownership of the socket resources. 493   /** Transfers ownership of the socket resources.
494   494  
495   @param other The socket to move from. 495   @param other The socket to move from.
496   */ 496   */
HITCBC 497   4 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {} 497   4 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {}
498   498  
499   /** Closes any existing socket and transfers ownership. 499   /** Closes any existing socket and transfers ownership.
500   500  
501   @param other The socket to move from. 501   @param other The socket to move from.
502   @return Reference to this socket. 502   @return Reference to this socket.
503   */ 503   */
HITCBC 504   2 udp_socket& operator=(udp_socket&& other) noexcept 504   2 udp_socket& operator=(udp_socket&& other) noexcept
505   { 505   {
HITCBC 506   2 if (this != &other) 506   2 if (this != &other)
507   { 507   {
HITCBC 508   2 close(); 508   2 close();
HITCBC 509   2 h_ = std::move(other.h_); 509   2 h_ = std::move(other.h_);
510   } 510   }
HITCBC 511   2 return *this; 511   2 return *this;
512   } 512   }
513   513  
514   /// Copy construction is disabled; the handle is uniquely owned. 514   /// Copy construction is disabled; the handle is uniquely owned.
515   udp_socket(udp_socket const&) = delete; 515   udp_socket(udp_socket const&) = delete;
516   /// Copy assignment is disabled; the handle is uniquely owned. 516   /// Copy assignment is disabled; the handle is uniquely owned.
517   udp_socket& operator=(udp_socket const&) = delete; 517   udp_socket& operator=(udp_socket const&) = delete;
518   518  
519   /** Open the socket. 519   /** Open the socket.
520   520  
521   Creates a UDP socket and associates it with the platform 521   Creates a UDP socket and associates it with the platform
522   reactor. 522   reactor.
523   523  
524   Failures such as descriptor exhaustion are normal runtime 524   Failures such as descriptor exhaustion are normal runtime
525   conditions and are reported through the returned error code. 525   conditions and are reported through the returned error code.
526   Opening an already-open socket is a no-op that reports 526   Opening an already-open socket is a no-op that reports
527   success. 527   success.
528   528  
529   @param f The address family (IPv4 or IPv6). Defaults to 529   @param f The address family (IPv4 or IPv6). Defaults to
530   `family::v4`. 530   `family::v4`.
531   531  
532   @return The error code, empty on success. 532   @return The error code, empty on success.
533   */ 533   */
534   [[nodiscard]] std::error_code open(family f = family::v4) noexcept; 534   [[nodiscard]] std::error_code open(family f = family::v4) noexcept;
535   535  
536   /** Close the socket. 536   /** Close the socket.
537   537  
538   Releases socket resources. Any pending operations complete 538   Releases socket resources. Any pending operations complete
539   with `errc::operation_canceled`. 539   with `errc::operation_canceled`.
540   */ 540   */
541   void close() noexcept; 541   void close() noexcept;
542   542  
543   /** Check if the socket is open. 543   /** Check if the socket is open.
544   544  
545   @return `true` if the socket is open and ready for operations. 545   @return `true` if the socket is open and ready for operations.
546   */ 546   */
HITCBC 547   1932 bool is_open() const noexcept 547   1932 bool is_open() const noexcept
548   { 548   {
549   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 549   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
550   return h_ && get().native_handle() != ~native_handle_type(0); 550   return h_ && get().native_handle() != ~native_handle_type(0);
551   #else 551   #else
HITCBC 552   1932 return h_ && get().native_handle() >= 0; 552   1932 return h_ && get().native_handle() >= 0;
553   #endif 553   #endif
554   } 554   }
555   555  
556   /** Bind the socket to a local endpoint. 556   /** Bind the socket to a local endpoint.
557   557  
558   Associates the socket with a local address and port. 558   Associates the socket with a local address and port.
559   Required before calling `recv_from`. 559   Required before calling `recv_from`.
560   560  
561   @param ep The local endpoint to bind to. 561   @param ep The local endpoint to bind to.
562   562  
563   @return Error code on failure, empty on success. 563   @return Error code on failure, empty on success.
564   564  
565   A closed socket reports `errc::bad_file_descriptor`. 565   A closed socket reports `errc::bad_file_descriptor`.
566   */ 566   */
567   [[nodiscard]] std::error_code bind(endpoint ep) noexcept; 567   [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
568   568  
569   /** Disable sends or receives on the socket. 569   /** Disable sends or receives on the socket.
570   570  
571   Failures such as an unconnected socket are normal runtime 571   Failures such as an unconnected socket are normal runtime
572   conditions and are reported through the returned error 572   conditions and are reported through the returned error
573   code. A closed socket reports `errc::bad_file_descriptor`. 573   code. A closed socket reports `errc::bad_file_descriptor`.
574   574  
575   @param what Determines which operations are no longer 575   @param what Determines which operations are no longer
576   allowed. 576   allowed.
577   577  
578   @return The error code, empty on success. 578   @return The error code, empty on success.
579   */ 579   */
580   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept; 580   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
581   581  
582   /** Cancel any pending asynchronous operations. 582   /** Cancel any pending asynchronous operations.
583   583  
584   Operations still in flight complete with 584   Operations still in flight complete with
585   `errc::operation_canceled`; an operation whose result is 585   `errc::operation_canceled`; an operation whose result is
586   already decided reports that result. Check 586   already decided reports that result. Check
587   `ec == cond::canceled` for portable comparison. 587   `ec == cond::canceled` for portable comparison.
588   */ 588   */
589   void cancel() noexcept; 589   void cancel() noexcept;
590   590  
591   /** Get the native socket handle. 591   /** Get the native socket handle.
592   592  
593   @return The native socket handle, or -1 if not open. 593   @return The native socket handle, or -1 if not open.
594   */ 594   */
595   native_handle_type native_handle() const noexcept; 595   native_handle_type native_handle() const noexcept;
596   596  
597   /** Assign an existing native socket to this object. 597   /** Assign an existing native socket to this object.
598   598  
599   Adopts a UDP socket created outside the library — received 599   Adopts a UDP socket created outside the library — received
600   from another process, inherited, or made natively — and 600   from another process, inherited, or made natively — and
601   registers it with the backend. The socket must be a datagram 601   registers it with the backend. The socket must be a datagram
602   socket in the `AF_INET` or `AF_INET6` family. Adoption never 602   socket in the `AF_INET` or `AF_INET6` family. Adoption never
603   alters the descriptor's flags or options: on POSIX the fd 603   alters the descriptor's flags or options: on POSIX the fd
604   must already be non-blocking, and on Windows the socket must 604   must already be non-blocking, and on Windows the socket must
605   be overlapped-capable. 605   be overlapped-capable.
606   606  
607   The object must be closed. To replace a held socket, `close()` 607   The object must be closed. To replace a held socket, `close()`
608   or `release()` it first. 608   or `release()` it first.
609   609  
610   @par Exception Safety 610   @par Exception Safety
611   Throws nothing. On failure the object is unchanged and the 611   Throws nothing. On failure the object is unchanged and the
612   caller retains ownership of `fd`. 612   caller retains ownership of `fd`.
613   613  
614   @param fd The native socket to adopt. On success the object 614   @param fd The native socket to adopt. On success the object
615   owns it and closes it. 615   owns it and closes it.
616   616  
617   @return `error::already_open` if this object is open. 617   @return `error::already_open` if this object is open.
618   Otherwise the error code, empty on success. Validation and 618   Otherwise the error code, empty on success. Validation and
619   registration failures are normal runtime conditions when 619   registration failures are normal runtime conditions when
620   adopting foreign descriptors. 620   adopting foreign descriptors.
621   */ 621   */
622   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 622   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
623   623  
624   /** Release ownership of the native socket handle. 624   /** Release ownership of the native socket handle.
625   625  
626   Deregisters the socket from the backend and cancels pending 626   Deregisters the socket from the backend and cancels pending
627   operations without closing the descriptor. The caller takes 627   operations without closing the descriptor. The caller takes
628   ownership of the returned handle. 628   ownership of the returned handle.
629   629  
630   A closed object reports `errc::bad_file_descriptor` and the 630   A closed object reports `errc::bad_file_descriptor` and the
631   returned handle is the invalid sentinel ( -1, or 631   returned handle is the invalid sentinel ( -1, or
632   `INVALID_SOCKET` on Windows ), never zero. 632   `INVALID_SOCKET` on Windows ), never zero.
633   633  
634   @note With the IOCP backend, a socket released while operations 634   @note With the IOCP backend, a socket released while operations
635   on it are in flight stays bound to this context's completion 635   on it are in flight stays bound to this context's completion
636   port for good. Windows refuses to unbind a handle with I/O 636   port for good. Windows refuses to unbind a handle with I/O
637   outstanding, so no context, this one included, can assign it 637   outstanding, so no context, this one included, can assign it
638   afterwards. Its cancelled operations still complete normally. 638   afterwards. Its cancelled operations still complete normally.
639   639  
640   @return The error code and the native handle. 640   @return The error code and the native handle.
641   641  
642   @post is_open() == false 642   @post is_open() == false
643   */ 643   */
644   [[nodiscard]] capy::results<native_handle_type> release() noexcept; 644   [[nodiscard]] capy::results<native_handle_type> release() noexcept;
645   645  
646   /** Set a socket option. 646   /** Set a socket option.
647   647  
648   Fails with `errc::bad_file_descriptor` if the socket is not 648   Fails with `errc::bad_file_descriptor` if the socket is not
649   open, or with the error the operating system reports. 649   open, or with the error the operating system reports.
650   650  
651   @param opt The option to set. 651   @param opt The option to set.
652   652  
653   @return The error code, empty on success. 653   @return The error code, empty on success.
654   */ 654   */
655   template<class Option> 655   template<class Option>
HITCBC 656   97 [[nodiscard]] std::error_code set_option(Option const& opt) noexcept 656   97 [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
657   { 657   {
HITCBC 658   97 if (!is_open()) 658   97 if (!is_open())
HITCBC 659   2 return make_error_code(std::errc::bad_file_descriptor); 659   2 return make_error_code(std::errc::bad_file_descriptor);
HITCBC 660   95 auto const fam = get().family(); 660   95 auto const fam = get().family();
HITCBC 661   95 return get().set_option( 661   95 return get().set_option(
HITCBC 662   95 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam)); 662   95 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
663   } 663   }
664   664  
665   /** Get a socket option. 665   /** Get a socket option.
666   666  
667   Fails with `errc::bad_file_descriptor` if the socket is not 667   Fails with `errc::bad_file_descriptor` if the socket is not
668   open, or with the error the operating system reports. On 668   open, or with the error the operating system reports. On
669   failure the option is value-initialized. 669   failure the option is value-initialized.
670   670  
671   @return The error code and the option value. 671   @return The error code and the option value.
672   */ 672   */
673   template<class Option> 673   template<class Option>
HITCBC 674   63 [[nodiscard]] capy::results<Option> get_option() const noexcept 674   63 [[nodiscard]] capy::results<Option> get_option() const noexcept
675   { 675   {
HITCBC 676   63 if (!is_open()) 676   63 if (!is_open())
HITCBC 677   2 return {make_error_code(std::errc::bad_file_descriptor), Option{}}; 677   2 return {make_error_code(std::errc::bad_file_descriptor), 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);
HITCBC 681   61 if (auto ec = get().get_option( 681   61 if (auto ec = get().get_option(
682   opt.level(fam), opt.name(fam), opt.data(fam), &sz)) 682   opt.level(fam), opt.name(fam), opt.data(fam), &sz))
HITCBC 683   2 return {ec, Option{}}; 683   2 return {ec, Option{}};
HITCBC 684   59 opt.resize(fam, sz); 684   59 opt.resize(fam, sz);
HITCBC 685   59 return {std::error_code{}, std::move(opt)}; 685   59 return {std::error_code{}, std::move(opt)};
686   } 686   }
687   687  
688   /** Get the local endpoint of the socket. 688   /** Get the local endpoint of the socket.
689   689  
690   @return The local endpoint, or a default endpoint if not bound. 690   @return The local endpoint, or a default endpoint if not bound.
691   */ 691   */
692   endpoint local_endpoint() const noexcept; 692   endpoint local_endpoint() const noexcept;
693   693  
694   /** Send a datagram to the specified destination. 694   /** Send a datagram to the specified destination.
695   695  
696   @param buf The buffer containing data to send. 696   @param buf The buffer containing data to send.
697   @param dest The destination endpoint. 697   @param dest The destination endpoint.
698   @param flags Message flags (e.g. message_flags::do_not_route). 698   @param flags Message flags (e.g. message_flags::do_not_route).
699   699  
700   @return An awaitable that completes with 700   @return An awaitable that completes with
701   `results<std::size_t>`. 701   `results<std::size_t>`.
702   702  
703   A closed socket reports `errc::bad_file_descriptor`. 703   A closed socket reports `errc::bad_file_descriptor`.
704   */ 704   */
705   template<capy::ConstBufferSequence Buffers> 705   template<capy::ConstBufferSequence Buffers>
706   [[nodiscard]] auto 706   [[nodiscard]] auto
HITCBC 707   79 send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags) 707   79 send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags)
708   { 708   {
HITCBC 709   79 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags)); 709   79 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
HITCBC 710   79 if (!is_open()) 710   79 if (!is_open())
HITCBC 711   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 711   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 712   79 return aw; 712   79 return aw;
713   } 713   }
714   714  
715   /// @overload 715   /// @overload
716   template<capy::ConstBufferSequence Buffers> 716   template<capy::ConstBufferSequence Buffers>
HITCBC 717   79 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest) 717   79 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest)
718   { 718   {
HITCBC 719   79 return send_to(buf, dest, corosio::message_flags::none); 719   79 return send_to(buf, dest, corosio::message_flags::none);
720   } 720   }
721   721  
722   /** Receive a datagram and capture the sender's endpoint. 722   /** Receive a datagram and capture the sender's endpoint.
723   723  
724   @param buf The buffer to receive data into. 724   @param buf The buffer to receive data into.
725   @param source Reference to an endpoint that receives 725   @param source Reference to an endpoint that receives
726   the sender's address on successful completion. 726   the sender's address on successful completion.
727   @param flags Message flags (e.g. message_flags::peek). 727   @param flags Message flags (e.g. message_flags::peek).
728   728  
729   @return An awaitable that completes with 729   @return An awaitable that completes with
730   `results<std::size_t>`. 730   `results<std::size_t>`.
731   731  
732   A closed socket reports `errc::bad_file_descriptor`. 732   A closed socket reports `errc::bad_file_descriptor`.
733   */ 733   */
734   template<capy::MutableBufferSequence Buffers> 734   template<capy::MutableBufferSequence Buffers>
HITCBC 735   99 [[nodiscard]] auto recv_from( 735   99 [[nodiscard]] auto recv_from(
736   Buffers const& buf, endpoint& source, corosio::message_flags flags) 736   Buffers const& buf, endpoint& source, corosio::message_flags flags)
737   { 737   {
HITCBC 738   99 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags)); 738   99 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
HITCBC 739   99 if (!is_open()) 739   99 if (!is_open())
HITCBC 740   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 740   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 741   99 return aw; 741   99 return aw;
742   } 742   }
743   743  
744   /// @overload 744   /// @overload
745   template<capy::MutableBufferSequence Buffers> 745   template<capy::MutableBufferSequence Buffers>
HITCBC 746   96 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source) 746   96 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source)
747   { 747   {
HITCBC 748   96 return recv_from(buf, source, corosio::message_flags::none); 748   96 return recv_from(buf, source, corosio::message_flags::none);
749   } 749   }
750   750  
751   /** Initiate an asynchronous connect to set the default peer. 751   /** Initiate an asynchronous connect to set the default peer.
752   752  
753   If the socket is not already open, it is opened automatically 753   If the socket is not already open, it is opened automatically
754   using the address family of @p ep. 754   using the address family of @p ep.
755   755  
756   @param ep The remote endpoint to connect to. 756   @param ep The remote endpoint to connect to.
757   757  
758   @return An awaitable that completes with `results<>`. 758   @return An awaitable that completes with `results<>`.
759   759  
760   If the socket needs to be opened and the open fails, the 760   If the socket needs to be opened and the open fails, the
761   awaitable completes immediately with that error. 761   awaitable completes immediately with that error.
762   */ 762   */
HITCBC 763   44 [[nodiscard]] auto connect(endpoint ep) 763   44 [[nodiscard]] auto connect(endpoint ep)
764   { 764   {
HITCBC 765   44 connect_awaitable aw(*this, ep); 765   44 connect_awaitable aw(*this, ep);
HITCBC 766   44 if (!is_open()) 766   44 if (!is_open())
HITCBC 767   10 aw.ec_ = open(ep.address().family()); 767   10 aw.ec_ = open(ep.address().family());
HITCBC 768   44 return aw; 768   44 return aw;
769   } 769   }
770   770  
771   /** Wait for the socket to become ready in a given direction. 771   /** Wait for the socket to become ready in a given direction.
772   772  
773   Suspends until the socket is ready for the requested 773   Suspends until the socket is ready for the requested
774   direction, or an error condition is reported. No bytes 774   direction, or an error condition is reported. No bytes
775   are transferred. 775   are transferred.
776   776  
777   The operation supports cancellation via `std::stop_token`. 777   The operation supports cancellation via `std::stop_token`.
778   778  
779   @param w The wait direction (read, write, or error). 779   @param w The wait direction (read, write, or error).
780   780  
781   @return An awaitable that completes with `results<>`. 781   @return An awaitable that completes with `results<>`.
782   782  
783   A closed socket completes with `errc::bad_file_descriptor`. 783   A closed socket completes with `errc::bad_file_descriptor`.
784   784  
785   @pre This socket must outlive the returned awaitable. 785   @pre This socket must outlive the returned awaitable.
786   */ 786   */
HITCBC 787   30 [[nodiscard]] auto wait(wait_type w) 787   30 [[nodiscard]] auto wait(wait_type w)
788   { 788   {
HITCBC 789   30 return wait_awaitable(*this, w); 789   30 return wait_awaitable(*this, w);
790   } 790   }
791   791  
792   /** Send a datagram to the connected peer. 792   /** Send a datagram to the connected peer.
793   793  
794   @param buf The buffer containing data to send. 794   @param buf The buffer containing data to send.
795   @param flags Message flags. 795   @param flags Message flags.
796   796  
797   @return An awaitable that completes with 797   @return An awaitable that completes with
798   `results<std::size_t>`. 798   `results<std::size_t>`.
799   799  
800   A closed socket reports `errc::bad_file_descriptor`. 800   A closed socket reports `errc::bad_file_descriptor`.
801   */ 801   */
802   template<capy::ConstBufferSequence Buffers> 802   template<capy::ConstBufferSequence Buffers>
HITCBC 803   28 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags) 803   28 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
804   { 804   {
HITCBC 805   28 send_awaitable aw(*this, buf, static_cast<int>(flags)); 805   28 send_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 806   28 if (!is_open()) 806   28 if (!is_open())
HITCBC 807   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 807   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 808   28 return aw; 808   28 return aw;
809   } 809   }
810   810  
811   /// @overload 811   /// @overload
812   template<capy::ConstBufferSequence Buffers> 812   template<capy::ConstBufferSequence Buffers>
HITCBC 813   28 [[nodiscard]] auto send(Buffers const& buf) 813   28 [[nodiscard]] auto send(Buffers const& buf)
814   { 814   {
HITCBC 815   28 return send(buf, corosio::message_flags::none); 815   28 return send(buf, corosio::message_flags::none);
816   } 816   }
817   817  
818   /** Receive a datagram from the connected peer. 818   /** Receive a datagram from the connected peer.
819   819  
820   @param buf The buffer to receive data into. 820   @param buf The buffer to receive data into.
821   @param flags Message flags (e.g. message_flags::peek). 821   @param flags Message flags (e.g. message_flags::peek).
822   822  
823   @return An awaitable that completes with 823   @return An awaitable that completes with
824   `results<std::size_t>`. 824   `results<std::size_t>`.
825   825  
826   A closed socket reports `errc::bad_file_descriptor`. 826   A closed socket reports `errc::bad_file_descriptor`.
827   */ 827   */
828   template<capy::MutableBufferSequence Buffers> 828   template<capy::MutableBufferSequence Buffers>
HITCBC 829   72 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags) 829   72 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
830   { 830   {
HITCBC 831   72 recv_awaitable aw(*this, buf, static_cast<int>(flags)); 831   72 recv_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 832   72 if (!is_open()) 832   72 if (!is_open())
HITCBC 833   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 833   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 834   72 return aw; 834   72 return aw;
835   } 835   }
836   836  
837   /// @overload 837   /// @overload
838   template<capy::MutableBufferSequence Buffers> 838   template<capy::MutableBufferSequence Buffers>
HITCBC 839   70 [[nodiscard]] auto recv(Buffers const& buf) 839   70 [[nodiscard]] auto recv(Buffers const& buf)
840   { 840   {
HITCBC 841   70 return recv(buf, corosio::message_flags::none); 841   70 return recv(buf, corosio::message_flags::none);
842   } 842   }
843   843  
844   /** Get the remote endpoint of the socket. 844   /** Get the remote endpoint of the socket.
845   845  
846   Returns the address and port of the connected peer. 846   Returns the address and port of the connected peer.
847   847  
848   @return The remote endpoint, or a default endpoint if 848   @return The remote endpoint, or a default endpoint if
849   not connected. 849   not connected.
850   */ 850   */
851   endpoint remote_endpoint() const noexcept; 851   endpoint remote_endpoint() const noexcept;
852   852  
853   protected: 853   protected:
854   /// Construct from a pre-built handle (for native_udp_socket). 854   /// Construct from a pre-built handle (for native_udp_socket).
HITCBC 855   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h)) 855   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
856   { 856   {
HITCBC 857   42 } 857   42 }
858   858  
859   private: 859   private:
860   /// Open the socket for the given protocol triple. 860   /// Open the socket for the given protocol triple.
861   [[nodiscard]] std::error_code 861   [[nodiscard]] std::error_code
862   open_for_family(int family, int type, int protocol) noexcept; 862   open_for_family(int family, int type, int protocol) noexcept;
863   863  
HITCBC 864   2795 inline implementation& get() const noexcept 864   2795 inline implementation& get() const noexcept
865   { 865   {
HITCBC 866   2795 return *static_cast<implementation*>(h_.get()); 866   2795 return *static_cast<implementation*>(h_.get());
867   } 867   }
868   }; 868   };
869   869  
870   } // namespace boost::corosio 870   } // namespace boost::corosio
871   871  
872   #endif // BOOST_COROSIO_UDP_SOCKET_HPP 872   #endif // BOOST_COROSIO_UDP_SOCKET_HPP