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