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