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