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   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   A closed object reports `errc::bad_file_descriptor` and the 628   A closed object reports `errc::bad_file_descriptor` and the
629   returned handle is the invalid sentinel ( -1, or 629   returned handle is the invalid sentinel ( -1, or
630   `INVALID_SOCKET` on Windows ), never zero. 630   `INVALID_SOCKET` on Windows ), never zero.
631   631  
632   @note With the IOCP backend, a socket released while operations 632   @note With the IOCP backend, a socket released while operations
633   on it are in flight stays bound to this context's completion 633   on it are in flight stays bound to this context's completion
634   port for good. Windows refuses to unbind a handle with I/O 634   port for good. Windows refuses to unbind a handle with I/O
635   outstanding, so no context, this one included, can assign it 635   outstanding, so no context, this one included, can assign it
636   afterwards. Its cancelled operations still complete normally. 636   afterwards. Its cancelled operations still complete normally.
637   637  
638   @return The error code and the native handle. 638   @return The error code and the native handle.
639   639  
640   @post is_open() == false 640   @post is_open() == false
641   */ 641   */
642   [[nodiscard]] capy::results<native_handle_type> release() noexcept; 642   [[nodiscard]] capy::results<native_handle_type> release() noexcept;
643   643  
644   /** Set a socket option. 644   /** Set a socket option.
645   645  
646   Fails with `errc::bad_file_descriptor` if the socket is not 646   Fails with `errc::bad_file_descriptor` if the socket is not
647   open, or with the error the operating system reports. 647   open, or with the error the operating system reports.
648   648  
649   @param opt The option to set. 649   @param opt The option to set.
650   650  
651   @return The error code, empty on success. 651   @return The error code, empty on success.
652   */ 652   */
653   template<class Option> 653   template<class Option>
HITCBC 654   97 [[nodiscard]] std::error_code set_option(Option const& opt) noexcept 654   97 [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
655   { 655   {
HITCBC 656   97 if (!is_open()) 656   97 if (!is_open())
HITCBC 657   2 return make_error_code(std::errc::bad_file_descriptor); 657   2 return make_error_code(std::errc::bad_file_descriptor);
HITCBC 658   95 auto const fam = get().family(); 658   95 auto const fam = get().family();
HITCBC 659   95 return get().set_option( 659   95 return get().set_option(
HITCBC 660   95 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam)); 660   95 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
661   } 661   }
662   662  
663   /** Get a socket option. 663   /** Get a socket option.
664   664  
665   Fails with `errc::bad_file_descriptor` if the socket is not 665   Fails with `errc::bad_file_descriptor` if the socket is not
666   open, or with the error the operating system reports. On 666   open, or with the error the operating system reports. On
667   failure the option is value-initialized. 667   failure the option is value-initialized.
668   668  
669   @return The error code and the option value. 669   @return The error code and the option value.
670   */ 670   */
671   template<class Option> 671   template<class Option>
HITCBC 672   63 [[nodiscard]] capy::results<Option> get_option() const noexcept 672   63 [[nodiscard]] capy::results<Option> get_option() const noexcept
673   { 673   {
HITCBC 674   63 if (!is_open()) 674   63 if (!is_open())
HITCBC 675   2 return {make_error_code(std::errc::bad_file_descriptor), Option{}}; 675   2 return {make_error_code(std::errc::bad_file_descriptor), Option{}};
HITCBC 676   61 Option opt{}; 676   61 Option opt{};
HITCBC 677   61 auto const fam = get().family(); 677   61 auto const fam = get().family();
HITCBC 678   61 std::size_t sz = opt.size(fam); 678   61 std::size_t sz = opt.size(fam);
HITCBC 679   61 if (auto ec = get().get_option( 679   61 if (auto ec = get().get_option(
680   opt.level(fam), opt.name(fam), opt.data(fam), &sz)) 680   opt.level(fam), opt.name(fam), opt.data(fam), &sz))
HITCBC 681   2 return {ec, Option{}}; 681   2 return {ec, Option{}};
HITCBC 682   59 opt.resize(fam, sz); 682   59 opt.resize(fam, sz);
HITCBC 683   59 return {std::error_code{}, std::move(opt)}; 683   59 return {std::error_code{}, std::move(opt)};
684   } 684   }
685   685  
686   /** Get the local endpoint of the socket. 686   /** Get the local endpoint of the socket.
687   687  
688   @return The local endpoint, or a default endpoint if not bound. 688   @return The local endpoint, or a default endpoint if not bound.
689   */ 689   */
690   endpoint local_endpoint() const noexcept; 690   endpoint local_endpoint() const noexcept;
691   691  
692   /** Send a datagram to the specified destination. 692   /** Send a datagram to the specified destination.
693   693  
694   @param buf The buffer containing data to send. 694   @param buf The buffer containing data to send.
695   @param dest The destination endpoint. 695   @param dest The destination endpoint.
696   @param flags Message flags (e.g. message_flags::do_not_route). 696   @param flags Message flags (e.g. message_flags::do_not_route).
697   697  
698   @return An awaitable that completes with 698   @return An awaitable that completes with
699   `results<std::size_t>`. 699   `results<std::size_t>`.
700   700  
701   A closed socket reports `errc::bad_file_descriptor`. 701   A closed socket reports `errc::bad_file_descriptor`.
702   */ 702   */
703   template<capy::ConstBufferSequence Buffers> 703   template<capy::ConstBufferSequence Buffers>
704   [[nodiscard]] auto 704   [[nodiscard]] auto
HITCBC 705   79 send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags) 705   79 send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags)
706   { 706   {
HITCBC 707   79 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags)); 707   79 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
HITCBC 708   79 if (!is_open()) 708   79 if (!is_open())
HITCBC 709   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 709   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 710   79 return aw; 710   79 return aw;
711   } 711   }
712   712  
713   /// @overload 713   /// @overload
714   template<capy::ConstBufferSequence Buffers> 714   template<capy::ConstBufferSequence Buffers>
HITCBC 715   79 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest) 715   79 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest)
716   { 716   {
HITCBC 717   79 return send_to(buf, dest, corosio::message_flags::none); 717   79 return send_to(buf, dest, corosio::message_flags::none);
718   } 718   }
719   719  
720   /** Receive a datagram and capture the sender's endpoint. 720   /** Receive a datagram and capture the sender's endpoint.
721   721  
722   @param buf The buffer to receive data into. 722   @param buf The buffer to receive data into.
723   @param source Reference to an endpoint that receives 723   @param source Reference to an endpoint that receives
724   the sender's address on successful completion. 724   the sender's address on successful completion.
725   @param flags Message flags (e.g. message_flags::peek). 725   @param flags Message flags (e.g. message_flags::peek).
726   726  
727   @return An awaitable that completes with 727   @return An awaitable that completes with
728   `results<std::size_t>`. 728   `results<std::size_t>`.
729   729  
730   A closed socket reports `errc::bad_file_descriptor`. 730   A closed socket reports `errc::bad_file_descriptor`.
731   */ 731   */
732   template<capy::MutableBufferSequence Buffers> 732   template<capy::MutableBufferSequence Buffers>
HITCBC 733   99 [[nodiscard]] auto recv_from( 733   99 [[nodiscard]] auto recv_from(
734   Buffers const& buf, endpoint& source, corosio::message_flags flags) 734   Buffers const& buf, endpoint& source, corosio::message_flags flags)
735   { 735   {
HITCBC 736   99 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags)); 736   99 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
HITCBC 737   99 if (!is_open()) 737   99 if (!is_open())
HITCBC 738   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 738   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 739   99 return aw; 739   99 return aw;
740   } 740   }
741   741  
742   /// @overload 742   /// @overload
743   template<capy::MutableBufferSequence Buffers> 743   template<capy::MutableBufferSequence Buffers>
HITCBC 744   96 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source) 744   96 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source)
745   { 745   {
HITCBC 746   96 return recv_from(buf, source, corosio::message_flags::none); 746   96 return recv_from(buf, source, corosio::message_flags::none);
747   } 747   }
748   748  
749   /** Initiate an asynchronous connect to set the default peer. 749   /** Initiate an asynchronous connect to set the default peer.
750   750  
751   If the socket is not already open, it is opened automatically 751   If the socket is not already open, it is opened automatically
752   using the address family of @p ep. 752   using the address family of @p ep.
753   753  
754   @param ep The remote endpoint to connect to. 754   @param ep The remote endpoint to connect to.
755   755  
756   @return An awaitable that completes with `results<>`. 756   @return An awaitable that completes with `results<>`.
757   757  
758   If the socket needs to be opened and the open fails, the 758   If the socket needs to be opened and the open fails, the
759   awaitable completes immediately with that error. 759   awaitable completes immediately with that error.
760   */ 760   */
HITCBC 761   44 [[nodiscard]] auto connect(endpoint ep) 761   44 [[nodiscard]] auto connect(endpoint ep)
762   { 762   {
HITCBC 763   44 connect_awaitable aw(*this, ep); 763   44 connect_awaitable aw(*this, ep);
HITCBC 764   44 if (!is_open()) 764   44 if (!is_open())
HITCBC 765   10 aw.ec_ = open(ep.address().family()); 765   10 aw.ec_ = open(ep.address().family());
HITCBC 766   44 return aw; 766   44 return aw;
767   } 767   }
768   768  
769   /** Wait for the socket to become ready in a given direction. 769   /** Wait for the socket to become ready in a given direction.
770   770  
771   Suspends until the socket is ready for the requested 771   Suspends until the socket is ready for the requested
772   direction, or an error condition is reported. No bytes 772   direction, or an error condition is reported. No bytes
773   are transferred. 773   are transferred.
774   774  
775   The operation supports cancellation via `std::stop_token`. 775   The operation supports cancellation via `std::stop_token`.
776   776  
777   @param w The wait direction (read, write, or error). 777   @param w The wait direction (read, write, or error).
778   778  
779   @return An awaitable that completes with `results<>`. 779   @return An awaitable that completes with `results<>`.
780   780  
781   A closed socket completes with `errc::bad_file_descriptor`. 781   A closed socket completes with `errc::bad_file_descriptor`.
782   782  
783   @pre This socket must outlive the returned awaitable. 783   @pre This socket must outlive the returned awaitable.
784   */ 784   */
HITCBC 785   30 [[nodiscard]] auto wait(wait_type w) 785   30 [[nodiscard]] auto wait(wait_type w)
786   { 786   {
HITCBC 787   30 return wait_awaitable(*this, w); 787   30 return wait_awaitable(*this, w);
788   } 788   }
789   789  
790   /** Send a datagram to the connected peer. 790   /** Send a datagram to the connected peer.
791   791  
792   @param buf The buffer containing data to send. 792   @param buf The buffer containing data to send.
793   @param flags Message flags. 793   @param flags Message flags.
794   794  
795   @return An awaitable that completes with 795   @return An awaitable that completes with
796   `results<std::size_t>`. 796   `results<std::size_t>`.
797   797  
798   A closed socket reports `errc::bad_file_descriptor`. 798   A closed socket reports `errc::bad_file_descriptor`.
799   */ 799   */
800   template<capy::ConstBufferSequence Buffers> 800   template<capy::ConstBufferSequence Buffers>
HITCBC 801   28 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags) 801   28 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
802   { 802   {
HITCBC 803   28 send_awaitable aw(*this, buf, static_cast<int>(flags)); 803   28 send_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 804   28 if (!is_open()) 804   28 if (!is_open())
HITCBC 805   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 805   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 806   28 return aw; 806   28 return aw;
807   } 807   }
808   808  
809   /// @overload 809   /// @overload
810   template<capy::ConstBufferSequence Buffers> 810   template<capy::ConstBufferSequence Buffers>
HITCBC 811   28 [[nodiscard]] auto send(Buffers const& buf) 811   28 [[nodiscard]] auto send(Buffers const& buf)
812   { 812   {
HITCBC 813   28 return send(buf, corosio::message_flags::none); 813   28 return send(buf, corosio::message_flags::none);
814   } 814   }
815   815  
816   /** Receive a datagram from the connected peer. 816   /** Receive a datagram from the connected peer.
817   817  
818   @param buf The buffer to receive data into. 818   @param buf The buffer to receive data into.
819   @param flags Message flags (e.g. message_flags::peek). 819   @param flags Message flags (e.g. message_flags::peek).
820   820  
821   @return An awaitable that completes with 821   @return An awaitable that completes with
822   `results<std::size_t>`. 822   `results<std::size_t>`.
823   823  
824   A closed socket reports `errc::bad_file_descriptor`. 824   A closed socket reports `errc::bad_file_descriptor`.
825   */ 825   */
826   template<capy::MutableBufferSequence Buffers> 826   template<capy::MutableBufferSequence Buffers>
HITCBC 827   72 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags) 827   72 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
828   { 828   {
HITCBC 829   72 recv_awaitable aw(*this, buf, static_cast<int>(flags)); 829   72 recv_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 830   72 if (!is_open()) 830   72 if (!is_open())
HITCBC 831   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 831   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 832   72 return aw; 832   72 return aw;
833   } 833   }
834   834  
835   /// @overload 835   /// @overload
836   template<capy::MutableBufferSequence Buffers> 836   template<capy::MutableBufferSequence Buffers>
HITCBC 837   70 [[nodiscard]] auto recv(Buffers const& buf) 837   70 [[nodiscard]] auto recv(Buffers const& buf)
838   { 838   {
HITCBC 839   70 return recv(buf, corosio::message_flags::none); 839   70 return recv(buf, corosio::message_flags::none);
840   } 840   }
841   841  
842   /** Get the remote endpoint of the socket. 842   /** Get the remote endpoint of the socket.
843   843  
844   Returns the address and port of the connected peer. 844   Returns the address and port of the connected peer.
845   845  
846   @return The remote endpoint, or a default endpoint if 846   @return The remote endpoint, or a default endpoint if
847   not connected. 847   not connected.
848   */ 848   */
849   endpoint remote_endpoint() const noexcept; 849   endpoint remote_endpoint() const noexcept;
850   850  
851   protected: 851   protected:
852   /// Construct from a pre-built handle (for native_udp_socket). 852   /// Construct from a pre-built handle (for native_udp_socket).
HITCBC 853   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h)) 853   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
854   { 854   {
HITCBC 855   42 } 855   42 }
856   856  
857   private: 857   private:
858   /// Open the socket for the given protocol triple. 858   /// Open the socket for the given protocol triple.
859   [[nodiscard]] std::error_code 859   [[nodiscard]] std::error_code
860   open_for_family(int family, int type, int protocol) noexcept; 860   open_for_family(int family, int type, int protocol) noexcept;
861   861  
HITCBC 862   2795 inline implementation& get() const noexcept 862   2795 inline implementation& get() const noexcept
863   { 863   {
HITCBC 864   2795 return *static_cast<implementation*>(h_.get()); 864   2795 return *static_cast<implementation*>(h_.get());
865   } 865   }
866   }; 866   };
867   867  
868   } // namespace boost::corosio 868   } // namespace boost::corosio
869   869  
870   #endif // BOOST_COROSIO_UDP_SOCKET_HPP 870   #endif // BOOST_COROSIO_UDP_SOCKET_HPP