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