90.59% Lines (77/85) 100.00% Functions (28/28)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_UDP_SOCKET_HPP 10   #ifndef BOOST_COROSIO_UDP_SOCKET_HPP
11   #define BOOST_COROSIO_UDP_SOCKET_HPP 11   #define BOOST_COROSIO_UDP_SOCKET_HPP
12   12  
13   #include <boost/corosio/detail/config.hpp> 13   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/detail/platform.hpp> 14   #include <boost/corosio/detail/platform.hpp>
15   #include <boost/corosio/detail/except.hpp> 15   #include <boost/corosio/detail/except.hpp>
16   #include <boost/corosio/detail/native_handle.hpp> 16   #include <boost/corosio/detail/native_handle.hpp>
17   #include <boost/corosio/detail/op_base.hpp> 17   #include <boost/corosio/detail/op_base.hpp>
18   #include <boost/corosio/io/io_object.hpp> 18   #include <boost/corosio/io/io_object.hpp>
19   #include <boost/capy/io_result.hpp> 19   #include <boost/capy/io_result.hpp>
20   #include <boost/corosio/detail/buffer_param.hpp> 20   #include <boost/corosio/detail/buffer_param.hpp>
21   #include <boost/corosio/endpoint.hpp> 21   #include <boost/corosio/endpoint.hpp>
22   #include <boost/corosio/message_flags.hpp> 22   #include <boost/corosio/message_flags.hpp>
23   #include <boost/corosio/udp.hpp> 23   #include <boost/corosio/udp.hpp>
24   #include <boost/corosio/wait_type.hpp> 24   #include <boost/corosio/wait_type.hpp>
25   #include <boost/capy/ex/executor_ref.hpp> 25   #include <boost/capy/ex/executor_ref.hpp>
26   #include <boost/capy/ex/execution_context.hpp> 26   #include <boost/capy/ex/execution_context.hpp>
27   #include <boost/capy/ex/io_env.hpp> 27   #include <boost/capy/ex/io_env.hpp>
28   #include <boost/capy/concept/executor.hpp> 28   #include <boost/capy/concept/executor.hpp>
29   29  
30   #include <system_error> 30   #include <system_error>
31   31  
32   #include <concepts> 32   #include <concepts>
33   #include <coroutine> 33   #include <coroutine>
34   #include <cstddef> 34   #include <cstddef>
35   #include <stop_token> 35   #include <stop_token>
36   #include <type_traits> 36   #include <type_traits>
37   37  
38   namespace boost::corosio { 38   namespace boost::corosio {
39   39  
40   /** An asynchronous UDP socket for coroutine I/O. 40   /** An asynchronous UDP socket for coroutine I/O.
41   41  
42   This class provides asynchronous UDP datagram operations that 42   This class provides asynchronous UDP datagram operations that
43   return awaitable types. Each operation participates in the affine 43   return awaitable types. Each operation participates in the affine
44   awaitable protocol, ensuring coroutines resume on the correct 44   awaitable protocol, ensuring coroutines resume on the correct
45   executor. 45   executor.
46   46  
47   Supports two modes of operation: 47   Supports two modes of operation:
48   48  
49   **Connectionless mode**: each `send_to` specifies a destination 49   **Connectionless mode**: each `send_to` specifies a destination
50   endpoint, and each `recv_from` captures the source endpoint. 50   endpoint, and each `recv_from` captures the source endpoint.
51   The socket must be opened (and optionally bound) before I/O. 51   The socket must be opened (and optionally bound) before I/O.
52   52  
53   **Connected mode**: call `connect()` to set a default peer, 53   **Connected mode**: call `connect()` to set a default peer,
54   then use `send()`/`recv()` without endpoint arguments. 54   then use `send()`/`recv()` without endpoint arguments.
55   The kernel filters incoming datagrams to those from the 55   The kernel filters incoming datagrams to those from the
56   connected peer. 56   connected peer.
57   57  
58   @par Thread Safety 58   @par Thread Safety
59   Distinct objects: Safe.@n 59   Distinct objects: Safe.@n
60   Shared objects: Unsafe. A socket must not have concurrent 60   Shared objects: Unsafe. A socket must not have concurrent
61   operations of the same type (e.g., two simultaneous recv_from). 61   operations of the same type (e.g., two simultaneous recv_from).
62   One send_to and one recv_from may be in flight simultaneously. 62   One send_to and one recv_from may be in flight simultaneously.
63   63  
64   @par Example 64   @par Example
65   @code 65   @code
66   // Connectionless mode 66   // Connectionless mode
67   io_context ioc; 67   io_context ioc;
68   udp_socket sock( ioc ); 68   udp_socket sock( ioc );
69   sock.open( udp::v4() ); 69   sock.open( udp::v4() );
70   sock.bind( endpoint( ipv4_address::any(), 9000 ) ); 70   sock.bind( endpoint( ipv4_address::any(), 9000 ) );
71   71  
72   char buf[1024]; 72   char buf[1024];
73   endpoint sender; 73   endpoint sender;
74   auto [ec, n] = co_await sock.recv_from( 74   auto [ec, n] = co_await sock.recv_from(
75   capy::mutable_buffer( buf, sizeof( buf ) ), sender ); 75   capy::mutable_buffer( buf, sizeof( buf ) ), sender );
76   if ( !ec ) 76   if ( !ec )
77   co_await sock.send_to( 77   co_await sock.send_to(
78   capy::const_buffer( buf, n ), sender ); 78   capy::const_buffer( buf, n ), sender );
79   79  
80   // Connected mode 80   // Connected mode
81   udp_socket csock( ioc ); 81   udp_socket csock( ioc );
82   auto [cec] = co_await csock.connect( 82   auto [cec] = co_await csock.connect(
83   endpoint( ipv4_address::loopback(), 9000 ) ); 83   endpoint( ipv4_address::loopback(), 9000 ) );
84   if ( !cec ) 84   if ( !cec )
85   co_await csock.send( 85   co_await csock.send(
86   capy::const_buffer( buf, n ) ); 86   capy::const_buffer( buf, n ) );
87   @endcode 87   @endcode
88   */ 88   */
89   class BOOST_COROSIO_DECL udp_socket : public io_object 89   class BOOST_COROSIO_DECL udp_socket : public io_object
90   { 90   {
91   public: 91   public:
92   /** Define backend hooks for UDP socket operations. 92   /** Define backend hooks for UDP socket operations.
93   93  
94   Platform backends (epoll, kqueue, select) derive from 94   Platform backends (epoll, kqueue, select) derive from
95   this to implement datagram I/O and option management. 95   this to implement datagram I/O and option management.
96   */ 96   */
97   struct implementation : io_object::implementation 97   struct implementation : io_object::implementation
98   { 98   {
99   /** Initiate an asynchronous send_to operation. 99   /** Initiate an asynchronous send_to operation.
100   100  
101   @param h Coroutine handle to resume on completion. 101   @param h Coroutine handle to resume on completion.
102   @param ex Executor for dispatching the completion. 102   @param ex Executor for dispatching the completion.
103   @param buf The buffer data to send. 103   @param buf The buffer data to send.
104   @param dest The destination endpoint. 104   @param dest The destination endpoint.
105   @param flags Platform message flags (e.g. `MSG_DONTWAIT`). 105   @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
106   @param token Stop token for cancellation. 106   @param token Stop token for cancellation.
107   @param ec Output error code. 107   @param ec Output error code.
108   @param bytes_out Output bytes transferred. 108   @param bytes_out Output bytes transferred.
109   109  
110   @return Coroutine handle to resume immediately. 110   @return Coroutine handle to resume immediately.
111   */ 111   */
112   virtual std::coroutine_handle<> send_to( 112   virtual std::coroutine_handle<> send_to(
113   std::coroutine_handle<> h, 113   std::coroutine_handle<> h,
114   capy::executor_ref ex, 114   capy::executor_ref ex,
115   buffer_param buf, 115   buffer_param buf,
116   endpoint dest, 116   endpoint dest,
117   int flags, 117   int flags,
118   std::stop_token token, 118   std::stop_token token,
119   std::error_code* ec, 119   std::error_code* ec,
120   std::size_t* bytes_out) = 0; 120   std::size_t* bytes_out) = 0;
121   121  
122   /** Initiate an asynchronous recv_from operation. 122   /** Initiate an asynchronous recv_from operation.
123   123  
124   @param h Coroutine handle to resume on completion. 124   @param h Coroutine handle to resume on completion.
125   @param ex Executor for dispatching the completion. 125   @param ex Executor for dispatching the completion.
126   @param buf The buffer to receive into. 126   @param buf The buffer to receive into.
127   @param source Output endpoint for the sender's address. 127   @param source Output endpoint for the sender's address.
128   @param flags Platform message flags (e.g. `MSG_PEEK`). 128   @param flags Platform message flags (e.g. `MSG_PEEK`).
129   @param token Stop token for cancellation. 129   @param token Stop token for cancellation.
130   @param ec Output error code. 130   @param ec Output error code.
131   @param bytes_out Output bytes transferred. 131   @param bytes_out Output bytes transferred.
132   132  
133   @return Coroutine handle to resume immediately. 133   @return Coroutine handle to resume immediately.
134   */ 134   */
135   virtual std::coroutine_handle<> recv_from( 135   virtual std::coroutine_handle<> recv_from(
136   std::coroutine_handle<> h, 136   std::coroutine_handle<> h,
137   capy::executor_ref ex, 137   capy::executor_ref ex,
138   buffer_param buf, 138   buffer_param buf,
139   endpoint* source, 139   endpoint* source,
140   int flags, 140   int flags,
141   std::stop_token token, 141   std::stop_token token,
142   std::error_code* ec, 142   std::error_code* ec,
143   std::size_t* bytes_out) = 0; 143   std::size_t* bytes_out) = 0;
144   144  
145   /// Return the platform socket descriptor. 145   /// Return the platform socket descriptor.
146   virtual native_handle_type native_handle() const noexcept = 0; 146   virtual native_handle_type native_handle() const noexcept = 0;
147   147  
148   /** Request cancellation of pending asynchronous operations. 148   /** Request cancellation of pending asynchronous operations.
149   149  
150   All outstanding operations complete with operation_canceled 150   All outstanding operations complete with operation_canceled
151   error. Check `ec == cond::canceled` for portable comparison. 151   error. Check `ec == cond::canceled` for portable comparison.
152   */ 152   */
153   virtual void cancel() noexcept = 0; 153   virtual void cancel() noexcept = 0;
154   154  
155   /** Set a socket option. 155   /** Set a socket option.
156   156  
157   @param level The protocol level (e.g. `SOL_SOCKET`). 157   @param level The protocol level (e.g. `SOL_SOCKET`).
158   @param optname The option name. 158   @param optname The option name.
159   @param data Pointer to the option value. 159   @param data Pointer to the option value.
160   @param size Size of the option value in bytes. 160   @param size Size of the option value in bytes.
161   @return Error code on failure, empty on success. 161   @return Error code on failure, empty on success.
162   */ 162   */
163   virtual std::error_code set_option( 163   virtual std::error_code set_option(
164   int level, 164   int level,
165   int optname, 165   int optname,
166   void const* data, 166   void const* data,
167   std::size_t size) noexcept = 0; 167   std::size_t size) noexcept = 0;
168   168  
169   /** Get a socket option. 169   /** Get a socket option.
170   170  
171   @param level The protocol level (e.g. `SOL_SOCKET`). 171   @param level The protocol level (e.g. `SOL_SOCKET`).
172   @param optname The option name. 172   @param optname The option name.
173   @param data Pointer to receive the option value. 173   @param data Pointer to receive the option value.
174   @param size On entry, the size of the buffer. On exit, 174   @param size On entry, the size of the buffer. On exit,
175   the size of the option value. 175   the size of the option value.
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 178   virtual std::error_code
179   get_option(int level, int optname, void* data, std::size_t* size) 179   get_option(int level, int optname, void* data, std::size_t* size)
180   const noexcept = 0; 180   const noexcept = 0;
181   181  
182   /// Return the cached local endpoint. 182   /// Return the cached local endpoint.
183   virtual endpoint local_endpoint() const noexcept = 0; 183   virtual endpoint local_endpoint() const noexcept = 0;
184   184  
185   /// Return the cached remote endpoint (connected mode). 185   /// Return the cached remote endpoint (connected mode).
186   virtual endpoint remote_endpoint() const noexcept = 0; 186   virtual endpoint remote_endpoint() const noexcept = 0;
187   187  
188   /** Initiate an asynchronous connect to set the default peer. 188   /** Initiate an asynchronous connect to set the default peer.
189   189  
190   @param h Coroutine handle to resume on completion. 190   @param h Coroutine handle to resume on completion.
191   @param ex Executor for dispatching the completion. 191   @param ex Executor for dispatching the completion.
192   @param ep The remote endpoint to connect to. 192   @param ep The remote endpoint to connect to.
193   @param token Stop token for cancellation. 193   @param token Stop token for cancellation.
194   @param ec Output error code. 194   @param ec Output error code.
195   195  
196   @return Coroutine handle to resume immediately. 196   @return Coroutine handle to resume immediately.
197   */ 197   */
198   virtual std::coroutine_handle<> connect( 198   virtual std::coroutine_handle<> connect(
199   std::coroutine_handle<> h, 199   std::coroutine_handle<> h,
200   capy::executor_ref ex, 200   capy::executor_ref ex,
201   endpoint ep, 201   endpoint ep,
202   std::stop_token token, 202   std::stop_token token,
203   std::error_code* ec) = 0; 203   std::error_code* ec) = 0;
204   204  
205   /** Initiate an asynchronous connected send operation. 205   /** Initiate an asynchronous connected send operation.
206   206  
207   @param h Coroutine handle to resume on completion. 207   @param h Coroutine handle to resume on completion.
208   @param ex Executor for dispatching the completion. 208   @param ex Executor for dispatching the completion.
209   @param buf The buffer data to send. 209   @param buf The buffer data to send.
210   @param flags Platform message flags (e.g. `MSG_DONTWAIT`). 210   @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
211   @param token Stop token for cancellation. 211   @param token Stop token for cancellation.
212   @param ec Output error code. 212   @param ec Output error code.
213   @param bytes_out Output bytes transferred. 213   @param bytes_out Output bytes transferred.
214   214  
215   @return Coroutine handle to resume immediately. 215   @return Coroutine handle to resume immediately.
216   */ 216   */
217   virtual std::coroutine_handle<> send( 217   virtual std::coroutine_handle<> send(
218   std::coroutine_handle<> h, 218   std::coroutine_handle<> h,
219   capy::executor_ref ex, 219   capy::executor_ref ex,
220   buffer_param buf, 220   buffer_param buf,
221   int flags, 221   int flags,
222   std::stop_token token, 222   std::stop_token token,
223   std::error_code* ec, 223   std::error_code* ec,
224   std::size_t* bytes_out) = 0; 224   std::size_t* bytes_out) = 0;
225   225  
226   /** Initiate an asynchronous connected recv operation. 226   /** Initiate an asynchronous connected recv operation.
227   227  
228   @param h Coroutine handle to resume on completion. 228   @param h Coroutine handle to resume on completion.
229   @param ex Executor for dispatching the completion. 229   @param ex Executor for dispatching the completion.
230   @param buf The buffer to receive into. 230   @param buf The buffer to receive into.
231   @param flags Platform message flags (e.g. `MSG_PEEK`). 231   @param flags Platform message flags (e.g. `MSG_PEEK`).
232   @param token Stop token for cancellation. 232   @param token Stop token for cancellation.
233   @param ec Output error code. 233   @param ec Output error code.
234   @param bytes_out Output bytes transferred. 234   @param bytes_out Output bytes transferred.
235   235  
236   @return Coroutine handle to resume immediately. 236   @return Coroutine handle to resume immediately.
237   */ 237   */
238   virtual std::coroutine_handle<> recv( 238   virtual std::coroutine_handle<> recv(
239   std::coroutine_handle<> h, 239   std::coroutine_handle<> h,
240   capy::executor_ref ex, 240   capy::executor_ref ex,
241   buffer_param buf, 241   buffer_param buf,
242   int flags, 242   int flags,
243   std::stop_token token, 243   std::stop_token token,
244   std::error_code* ec, 244   std::error_code* ec,
245   std::size_t* bytes_out) = 0; 245   std::size_t* bytes_out) = 0;
246   246  
247   /** Initiate an asynchronous wait for socket readiness. 247   /** Initiate an asynchronous wait for socket readiness.
248   248  
249   Completes when the socket becomes ready for the 249   Completes when the socket becomes ready for the
250   specified direction, or an error condition is 250   specified direction, or an error condition is
251   reported. No bytes are transferred. 251   reported. No bytes are transferred.
252   252  
253   @param h Coroutine handle to resume on completion. 253   @param h Coroutine handle to resume on completion.
254   @param ex Executor for dispatching the completion. 254   @param ex Executor for dispatching the completion.
255   @param w The direction to wait on. 255   @param w The direction to wait on.
256   @param token Stop token for cancellation. 256   @param token Stop token for cancellation.
257   @param ec Output error code. 257   @param ec Output error code.
258   258  
259   @return Coroutine handle to resume immediately. 259   @return Coroutine handle to resume immediately.
260   */ 260   */
261   virtual std::coroutine_handle<> wait( 261   virtual std::coroutine_handle<> wait(
262   std::coroutine_handle<> h, 262   std::coroutine_handle<> h,
263   capy::executor_ref ex, 263   capy::executor_ref ex,
264   wait_type w, 264   wait_type w,
265   std::stop_token token, 265   std::stop_token token,
266   std::error_code* ec) = 0; 266   std::error_code* ec) = 0;
267   }; 267   };
268   268  
269   /** Represent the awaitable returned by @ref send_to. 269   /** Represent the awaitable returned by @ref send_to.
270   270  
271   Captures the destination endpoint and buffer, then dispatches 271   Captures the destination endpoint and buffer, then dispatches
272   to the backend implementation on suspension. 272   to the backend implementation on suspension.
273   */ 273   */
274   struct send_to_awaitable 274   struct send_to_awaitable
275   : detail::bytes_op_base<send_to_awaitable> 275   : detail::bytes_op_base<send_to_awaitable>
276   { 276   {
277   udp_socket& s_; 277   udp_socket& s_;
278   buffer_param buf_; 278   buffer_param buf_;
279   endpoint dest_; 279   endpoint dest_;
280   int flags_; 280   int flags_;
281   281  
HITCBC 282   24 send_to_awaitable( 282   24 send_to_awaitable(
283   udp_socket& s, buffer_param buf, 283   udp_socket& s, buffer_param buf,
284   endpoint dest, int flags = 0) noexcept 284   endpoint dest, int flags = 0) noexcept
HITCBC 285   24 : s_(s), buf_(buf), dest_(dest), flags_(flags) {} 285   24 : s_(s), buf_(buf), dest_(dest), flags_(flags) {}
286   286  
HITCBC 287   24 std::coroutine_handle<> dispatch( 287   24 std::coroutine_handle<> dispatch(
288   std::coroutine_handle<> h, capy::executor_ref ex) const 288   std::coroutine_handle<> h, capy::executor_ref ex) const
289   { 289   {
HITCBC 290   48 return s_.get().send_to( 290   48 return s_.get().send_to(
HITCBC 291   48 h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_); 291   48 h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
292   } 292   }
293   }; 293   };
294   294  
295   /** Represent the awaitable returned by @ref recv_from. 295   /** Represent the awaitable returned by @ref recv_from.
296   296  
297   Captures the source endpoint reference and buffer, then 297   Captures the source endpoint reference and buffer, then
298   dispatches to the backend implementation on suspension. 298   dispatches to the backend implementation on suspension.
299   */ 299   */
300   struct recv_from_awaitable 300   struct recv_from_awaitable
301   : detail::bytes_op_base<recv_from_awaitable> 301   : detail::bytes_op_base<recv_from_awaitable>
302   { 302   {
303   udp_socket& s_; 303   udp_socket& s_;
304   buffer_param buf_; 304   buffer_param buf_;
305   endpoint& source_; 305   endpoint& source_;
306   int flags_; 306   int flags_;
307   307  
HITCBC 308   32 recv_from_awaitable( 308   32 recv_from_awaitable(
309   udp_socket& s, buffer_param buf, 309   udp_socket& s, buffer_param buf,
310   endpoint& source, int flags = 0) noexcept 310   endpoint& source, int flags = 0) noexcept
HITCBC 311   32 : s_(s), buf_(buf), source_(source), flags_(flags) {} 311   32 : s_(s), buf_(buf), source_(source), flags_(flags) {}
312   312  
HITCBC 313   32 std::coroutine_handle<> dispatch( 313   32 std::coroutine_handle<> dispatch(
314   std::coroutine_handle<> h, capy::executor_ref ex) const 314   std::coroutine_handle<> h, capy::executor_ref ex) const
315   { 315   {
HITCBC 316   64 return s_.get().recv_from( 316   64 return s_.get().recv_from(
HITCBC 317   64 h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_); 317   64 h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
318   } 318   }
319   }; 319   };
320   320  
321   /// Represent the awaitable returned by @ref connect. 321   /// Represent the awaitable returned by @ref connect.
322   struct connect_awaitable 322   struct connect_awaitable
323   : detail::void_op_base<connect_awaitable> 323   : detail::void_op_base<connect_awaitable>
324   { 324   {
325   udp_socket& s_; 325   udp_socket& s_;
326   endpoint endpoint_; 326   endpoint endpoint_;
327   327  
HITCBC 328   12 connect_awaitable(udp_socket& s, endpoint ep) noexcept 328   12 connect_awaitable(udp_socket& s, endpoint ep) noexcept
HITCBC 329   12 : s_(s), endpoint_(ep) {} 329   12 : s_(s), endpoint_(ep) {}
330   330  
HITCBC 331   12 std::coroutine_handle<> dispatch( 331   12 std::coroutine_handle<> dispatch(
332   std::coroutine_handle<> h, capy::executor_ref ex) const 332   std::coroutine_handle<> h, capy::executor_ref ex) const
333   { 333   {
HITCBC 334   12 return s_.get().connect(h, ex, endpoint_, token_, &ec_); 334   12 return s_.get().connect(h, ex, endpoint_, token_, &ec_);
335   } 335   }
336   }; 336   };
337   337  
338   /// Represent the awaitable returned by @ref wait. 338   /// Represent the awaitable returned by @ref wait.
339   struct wait_awaitable 339   struct wait_awaitable
340   : detail::void_op_base<wait_awaitable> 340   : detail::void_op_base<wait_awaitable>
341   { 341   {
342   udp_socket& s_; 342   udp_socket& s_;
343   wait_type w_; 343   wait_type w_;
344   344  
HITCBC 345   4 wait_awaitable(udp_socket& s, wait_type w) noexcept 345   4 wait_awaitable(udp_socket& s, wait_type w) noexcept
HITCBC 346   4 : s_(s), w_(w) {} 346   4 : s_(s), w_(w) {}
347   347  
HITCBC 348   4 std::coroutine_handle<> dispatch( 348   4 std::coroutine_handle<> dispatch(
349   std::coroutine_handle<> h, capy::executor_ref ex) const 349   std::coroutine_handle<> h, capy::executor_ref ex) const
350   { 350   {
HITCBC 351   4 return s_.get().wait(h, ex, w_, token_, &ec_); 351   4 return s_.get().wait(h, ex, w_, token_, &ec_);
352   } 352   }
353   }; 353   };
354   354  
355   /// Represent the awaitable returned by @ref send. 355   /// Represent the awaitable returned by @ref send.
356   struct send_awaitable 356   struct send_awaitable
357   : detail::bytes_op_base<send_awaitable> 357   : detail::bytes_op_base<send_awaitable>
358   { 358   {
359   udp_socket& s_; 359   udp_socket& s_;
360   buffer_param buf_; 360   buffer_param buf_;
361   int flags_; 361   int flags_;
362   362  
HITCBC 363   6 send_awaitable( 363   6 send_awaitable(
364   udp_socket& s, buffer_param buf, 364   udp_socket& s, buffer_param buf,
365   int flags = 0) noexcept 365   int flags = 0) noexcept
HITCBC 366   6 : s_(s), buf_(buf), flags_(flags) {} 366   6 : s_(s), buf_(buf), flags_(flags) {}
367   367  
HITCBC 368   6 std::coroutine_handle<> dispatch( 368   6 std::coroutine_handle<> dispatch(
369   std::coroutine_handle<> h, capy::executor_ref ex) const 369   std::coroutine_handle<> h, capy::executor_ref ex) const
370   { 370   {
HITCBC 371   12 return s_.get().send( 371   12 return s_.get().send(
HITCBC 372   12 h, ex, buf_, flags_, token_, &ec_, &bytes_); 372   12 h, ex, buf_, flags_, token_, &ec_, &bytes_);
373   } 373   }
374   }; 374   };
375   375  
376   /// Represent the awaitable returned by @ref recv. 376   /// Represent the awaitable returned by @ref recv.
377   struct recv_awaitable 377   struct recv_awaitable
378   : detail::bytes_op_base<recv_awaitable> 378   : detail::bytes_op_base<recv_awaitable>
379   { 379   {
380   udp_socket& s_; 380   udp_socket& s_;
381   buffer_param buf_; 381   buffer_param buf_;
382   int flags_; 382   int flags_;
383   383  
HITCBC 384   4 recv_awaitable( 384   4 recv_awaitable(
385   udp_socket& s, buffer_param buf, 385   udp_socket& s, buffer_param buf,
386   int flags = 0) noexcept 386   int flags = 0) noexcept
HITCBC 387   4 : s_(s), buf_(buf), flags_(flags) {} 387   4 : s_(s), buf_(buf), flags_(flags) {}
388   388  
HITCBC 389   4 std::coroutine_handle<> dispatch( 389   4 std::coroutine_handle<> dispatch(
390   std::coroutine_handle<> h, capy::executor_ref ex) const 390   std::coroutine_handle<> h, capy::executor_ref ex) const
391   { 391   {
HITCBC 392   8 return s_.get().recv( 392   8 return s_.get().recv(
HITCBC 393   8 h, ex, buf_, flags_, token_, &ec_, &bytes_); 393   8 h, ex, buf_, flags_, token_, &ec_, &bytes_);
394   } 394   }
395   }; 395   };
396   396  
397   public: 397   public:
398   /** Destructor. 398   /** Destructor.
399   399  
400   Closes the socket if open, cancelling any pending operations. 400   Closes the socket if open, cancelling any pending operations.
401   */ 401   */
402   ~udp_socket() override; 402   ~udp_socket() override;
403   403  
404   /** Construct a socket from an execution context. 404   /** Construct a socket from an execution context.
405   405  
406   @param ctx The execution context that will own this socket. 406   @param ctx The execution context that will own this socket.
407   */ 407   */
408   explicit udp_socket(capy::execution_context& ctx); 408   explicit udp_socket(capy::execution_context& ctx);
409   409  
410   /** Construct a socket from an executor. 410   /** Construct a socket from an executor.
411   411  
412   The socket is associated with the executor's context. 412   The socket is associated with the executor's context.
413   413  
414   @param ex The executor whose context will own the socket. 414   @param ex The executor whose context will own the socket.
415   */ 415   */
416   template<class Ex> 416   template<class Ex>
417   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) && 417   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) &&
418   capy::Executor<Ex> 418   capy::Executor<Ex>
419   explicit udp_socket(Ex const& ex) : udp_socket(ex.context()) 419   explicit udp_socket(Ex const& ex) : udp_socket(ex.context())
420   { 420   {
421   } 421   }
422   422  
423   /** Move constructor. 423   /** Move constructor.
424   424  
425   Transfers ownership of the socket resources. 425   Transfers ownership of the socket resources.
426   426  
427   @param other The socket to move from. 427   @param other The socket to move from.
428   */ 428   */
HITCBC 429   2 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {} 429   2 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {}
430   430  
431   /** Move assignment operator. 431   /** Move assignment operator.
432   432  
433   Closes any existing socket and transfers ownership. 433   Closes any existing socket and transfers ownership.
434   434  
435   @param other The socket to move from. 435   @param other The socket to move from.
436   @return Reference to this socket. 436   @return Reference to this socket.
437   */ 437   */
HITCBC 438   2 udp_socket& operator=(udp_socket&& other) noexcept 438   2 udp_socket& operator=(udp_socket&& other) noexcept
439   { 439   {
HITCBC 440   2 if (this != &other) 440   2 if (this != &other)
441   { 441   {
HITCBC 442   2 close(); 442   2 close();
HITCBC 443   2 h_ = std::move(other.h_); 443   2 h_ = std::move(other.h_);
444   } 444   }
HITCBC 445   2 return *this; 445   2 return *this;
446   } 446   }
447   447  
448   udp_socket(udp_socket const&) = delete; 448   udp_socket(udp_socket const&) = delete;
449   udp_socket& operator=(udp_socket const&) = delete; 449   udp_socket& operator=(udp_socket const&) = delete;
450   450  
451   /** Open the socket. 451   /** Open the socket.
452   452  
453   Creates a UDP socket and associates it with the platform 453   Creates a UDP socket and associates it with the platform
454   reactor. 454   reactor.
455   455  
456   @param proto The protocol (IPv4 or IPv6). Defaults to 456   @param proto The protocol (IPv4 or IPv6). Defaults to
457   `udp::v4()`. 457   `udp::v4()`.
458   458  
459   @throws std::system_error on failure. 459   @throws std::system_error on failure.
460   */ 460   */
461   void open(udp proto = udp::v4()); 461   void open(udp proto = udp::v4());
462   462  
463   /** Close the socket. 463   /** Close the socket.
464   464  
465   Releases socket resources. Any pending operations complete 465   Releases socket resources. Any pending operations complete
466   with `errc::operation_canceled`. 466   with `errc::operation_canceled`.
467   */ 467   */
468   void close(); 468   void close();
469   469  
470   /** Check if the socket is open. 470   /** Check if the socket is open.
471   471  
472   @return `true` if the socket is open and ready for operations. 472   @return `true` if the socket is open and ready for operations.
473   */ 473   */
HITCBC 474   466 bool is_open() const noexcept 474   466 bool is_open() const noexcept
475   { 475   {
476   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 476   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
477   return h_ && get().native_handle() != ~native_handle_type(0); 477   return h_ && get().native_handle() != ~native_handle_type(0);
478   #else 478   #else
HITCBC 479   466 return h_ && get().native_handle() >= 0; 479   466 return h_ && get().native_handle() >= 0;
480   #endif 480   #endif
481   } 481   }
482   482  
483   /** Bind the socket to a local endpoint. 483   /** Bind the socket to a local endpoint.
484   484  
485   Associates the socket with a local address and port. 485   Associates the socket with a local address and port.
486   Required before calling `recv_from`. 486   Required before calling `recv_from`.
487   487  
488   @param ep The local endpoint to bind to. 488   @param ep The local endpoint to bind to.
489   489  
490   @return Error code on failure, empty on success. 490   @return Error code on failure, empty on success.
491   491  
492   @throws std::logic_error if the socket is not open. 492   @throws std::logic_error if the socket is not open.
493   */ 493   */
494   [[nodiscard]] std::error_code bind(endpoint ep); 494   [[nodiscard]] std::error_code bind(endpoint ep);
495   495  
496   /** Cancel any pending asynchronous operations. 496   /** Cancel any pending asynchronous operations.
497   497  
498   All outstanding operations complete with 498   All outstanding operations complete with
499   `errc::operation_canceled`. Check `ec == cond::canceled` 499   `errc::operation_canceled`. Check `ec == cond::canceled`
500   for portable comparison. 500   for portable comparison.
501   */ 501   */
502   void cancel(); 502   void cancel();
503   503  
504   /** Get the native socket handle. 504   /** Get the native socket handle.
505   505  
506   @return The native socket handle, or -1 if not open. 506   @return The native socket handle, or -1 if not open.
507   */ 507   */
508   native_handle_type native_handle() const noexcept; 508   native_handle_type native_handle() const noexcept;
509   509  
510   /** Set a socket option. 510   /** Set a socket option.
511   511  
512   @param opt The option to set. 512   @param opt The option to set.
513   513  
514   @throws std::logic_error if the socket is not open. 514   @throws std::logic_error if the socket is not open.
515   @throws std::system_error on failure. 515   @throws std::system_error on failure.
516   */ 516   */
517   template<class Option> 517   template<class Option>
HITCBC 518   20 void set_option(Option const& opt) 518   20 void set_option(Option const& opt)
519   { 519   {
HITCBC 520   20 if (!is_open()) 520   20 if (!is_open())
MISUBC 521   detail::throw_logic_error("set_option: socket not open"); 521   detail::throw_logic_error("set_option: socket not open");
HITCBC 522   20 std::error_code ec = get().set_option( 522   20 std::error_code ec = get().set_option(
523   Option::level(), Option::name(), opt.data(), opt.size()); 523   Option::level(), Option::name(), opt.data(), opt.size());
HITCBC 524   20 if (ec) 524   20 if (ec)
MISUBC 525   detail::throw_system_error(ec, "udp_socket::set_option"); 525   detail::throw_system_error(ec, "udp_socket::set_option");
HITCBC 526   20 } 526   20 }
527   527  
528   /** Get a socket option. 528   /** Get a socket option.
529   529  
530   @return The current option value. 530   @return The current option value.
531   531  
532   @throws std::logic_error if the socket is not open. 532   @throws std::logic_error if the socket is not open.
533   @throws std::system_error on failure. 533   @throws std::system_error on failure.
534   */ 534   */
535   template<class Option> 535   template<class Option>
HITCBC 536   16 Option get_option() const 536   16 Option get_option() const
537   { 537   {
HITCBC 538   16 if (!is_open()) 538   16 if (!is_open())
MISUBC 539   detail::throw_logic_error("get_option: socket not open"); 539   detail::throw_logic_error("get_option: socket not open");
HITCBC 540   16 Option opt{}; 540   16 Option opt{};
HITCBC 541   16 std::size_t sz = opt.size(); 541   16 std::size_t sz = opt.size();
542   std::error_code ec = 542   std::error_code ec =
HITCBC 543   16 get().get_option(Option::level(), Option::name(), opt.data(), &sz); 543   16 get().get_option(Option::level(), Option::name(), opt.data(), &sz);
HITCBC 544   16 if (ec) 544   16 if (ec)
MISUBC 545   detail::throw_system_error(ec, "udp_socket::get_option"); 545   detail::throw_system_error(ec, "udp_socket::get_option");
HITCBC 546   16 opt.resize(sz); 546   16 opt.resize(sz);
HITCBC 547   16 return opt; 547   16 return opt;
548   } 548   }
549   549  
550   /** Get the local endpoint of the socket. 550   /** Get the local endpoint of the socket.
551   551  
552   @return The local endpoint, or a default endpoint if not bound. 552   @return The local endpoint, or a default endpoint if not bound.
553   */ 553   */
554   endpoint local_endpoint() const noexcept; 554   endpoint local_endpoint() const noexcept;
555   555  
556   /** Send a datagram to the specified destination. 556   /** Send a datagram to the specified destination.
557   557  
558   @param buf The buffer containing data to send. 558   @param buf The buffer containing data to send.
559   @param dest The destination endpoint. 559   @param dest The destination endpoint.
560   @param flags Message flags (e.g. message_flags::dont_route). 560   @param flags Message flags (e.g. message_flags::dont_route).
561   561  
562   @return An awaitable that completes with 562   @return An awaitable that completes with
563   `io_result<std::size_t>`. 563   `io_result<std::size_t>`.
564   564  
565   @throws std::logic_error if the socket is not open. 565   @throws std::logic_error if the socket is not open.
566   */ 566   */
567   template<capy::ConstBufferSequence Buffers> 567   template<capy::ConstBufferSequence Buffers>
HITCBC 568   24 auto send_to( 568   24 auto send_to(
569   Buffers const& buf, 569   Buffers const& buf,
570   endpoint dest, 570   endpoint dest,
571   corosio::message_flags flags) 571   corosio::message_flags flags)
572   { 572   {
HITCBC 573   24 if (!is_open()) 573   24 if (!is_open())
MISUBC 574   detail::throw_logic_error("send_to: socket not open"); 574   detail::throw_logic_error("send_to: socket not open");
575   return send_to_awaitable( 575   return send_to_awaitable(
HITCBC 576   24 *this, buf, dest, static_cast<int>(flags)); 576   24 *this, buf, dest, static_cast<int>(flags));
577   } 577   }
578   578  
579   /// @overload 579   /// @overload
580   template<capy::ConstBufferSequence Buffers> 580   template<capy::ConstBufferSequence Buffers>
HITCBC 581   24 auto send_to(Buffers const& buf, endpoint dest) 581   24 auto send_to(Buffers const& buf, endpoint dest)
582   { 582   {
HITCBC 583   24 return send_to(buf, dest, corosio::message_flags::none); 583   24 return send_to(buf, dest, corosio::message_flags::none);
584   } 584   }
585   585  
586   /** Receive a datagram and capture the sender's endpoint. 586   /** Receive a datagram and capture the sender's endpoint.
587   587  
588   @param buf The buffer to receive data into. 588   @param buf The buffer to receive data into.
589   @param source Reference to an endpoint that will be set to 589   @param source Reference to an endpoint that will be set to
590   the sender's address on successful completion. 590   the sender's address on successful completion.
591   @param flags Message flags (e.g. message_flags::peek). 591   @param flags Message flags (e.g. message_flags::peek).
592   592  
593   @return An awaitable that completes with 593   @return An awaitable that completes with
594   `io_result<std::size_t>`. 594   `io_result<std::size_t>`.
595   595  
596   @throws std::logic_error if the socket is not open. 596   @throws std::logic_error if the socket is not open.
597   */ 597   */
598   template<capy::MutableBufferSequence Buffers> 598   template<capy::MutableBufferSequence Buffers>
HITCBC 599   32 auto recv_from( 599   32 auto recv_from(
600   Buffers const& buf, 600   Buffers const& buf,
601   endpoint& source, 601   endpoint& source,
602   corosio::message_flags flags) 602   corosio::message_flags flags)
603   { 603   {
HITCBC 604   32 if (!is_open()) 604   32 if (!is_open())
MISUBC 605   detail::throw_logic_error("recv_from: socket not open"); 605   detail::throw_logic_error("recv_from: socket not open");
606   return recv_from_awaitable( 606   return recv_from_awaitable(
HITCBC 607   32 *this, buf, source, static_cast<int>(flags)); 607   32 *this, buf, source, static_cast<int>(flags));
608   } 608   }
609   609  
610   /// @overload 610   /// @overload
611   template<capy::MutableBufferSequence Buffers> 611   template<capy::MutableBufferSequence Buffers>
HITCBC 612   32 auto recv_from(Buffers const& buf, endpoint& source) 612   32 auto recv_from(Buffers const& buf, endpoint& source)
613   { 613   {
HITCBC 614   32 return recv_from(buf, source, corosio::message_flags::none); 614   32 return recv_from(buf, source, corosio::message_flags::none);
615   } 615   }
616   616  
617   /** Initiate an asynchronous connect to set the default peer. 617   /** Initiate an asynchronous connect to set the default peer.
618   618  
619   If the socket is not already open, it is opened automatically 619   If the socket is not already open, it is opened automatically
620   using the address family of @p ep. 620   using the address family of @p ep.
621   621  
622   @param ep The remote endpoint to connect to. 622   @param ep The remote endpoint to connect to.
623   623  
624   @return An awaitable that completes with `io_result<>`. 624   @return An awaitable that completes with `io_result<>`.
625   625  
626   @throws std::system_error if the socket needs to be opened 626   @throws std::system_error if the socket needs to be opened
627   and the open fails. 627   and the open fails.
628   */ 628   */
HITCBC 629   12 auto connect(endpoint ep) 629   12 auto connect(endpoint ep)
630   { 630   {
HITCBC 631   12 if (!is_open()) 631   12 if (!is_open())
HITCBC 632   8 open(ep.is_v6() ? udp::v6() : udp::v4()); 632   8 open(ep.is_v6() ? udp::v6() : udp::v4());
HITCBC 633   12 return connect_awaitable(*this, ep); 633   12 return connect_awaitable(*this, ep);
634   } 634   }
635   635  
636   /** Wait for the socket to become ready in a given direction. 636   /** Wait for the socket to become ready in a given direction.
637   637  
638   Suspends until the socket is ready for the requested 638   Suspends until the socket is ready for the requested
639   direction, or an error condition is reported. No bytes 639   direction, or an error condition is reported. No bytes
640   are transferred. 640   are transferred.
641   641  
642   The operation supports cancellation via `std::stop_token`. 642   The operation supports cancellation via `std::stop_token`.
643   643  
644   @param w The wait direction (read, write, or error). 644   @param w The wait direction (read, write, or error).
645   645  
646   @return An awaitable that completes with `io_result<>`. 646   @return An awaitable that completes with `io_result<>`.
647   647  
648   @par Preconditions 648   @par Preconditions
649   The socket must be open. This socket must outlive the 649   The socket must be open. This socket must outlive the
650   returned awaitable. 650   returned awaitable.
651   */ 651   */
HITCBC 652   4 [[nodiscard]] auto wait(wait_type w) 652   4 [[nodiscard]] auto wait(wait_type w)
653   { 653   {
HITCBC 654   4 return wait_awaitable(*this, w); 654   4 return wait_awaitable(*this, w);
655   } 655   }
656   656  
657   /** Send a datagram to the connected peer. 657   /** Send a datagram to the connected peer.
658   658  
659   @param buf The buffer containing data to send. 659   @param buf The buffer containing data to send.
660   @param flags Message flags. 660   @param flags Message flags.
661   661  
662   @return An awaitable that completes with 662   @return An awaitable that completes with
663   `io_result<std::size_t>`. 663   `io_result<std::size_t>`.
664   664  
665   @throws std::logic_error if the socket is not open. 665   @throws std::logic_error if the socket is not open.
666   */ 666   */
667   template<capy::ConstBufferSequence Buffers> 667   template<capy::ConstBufferSequence Buffers>
HITCBC 668   6 auto send(Buffers const& buf, corosio::message_flags flags) 668   6 auto send(Buffers const& buf, corosio::message_flags flags)
669   { 669   {
HITCBC 670   6 if (!is_open()) 670   6 if (!is_open())
MISUBC 671   detail::throw_logic_error("send: socket not open"); 671   detail::throw_logic_error("send: socket not open");
672   return send_awaitable( 672   return send_awaitable(
HITCBC 673   6 *this, buf, static_cast<int>(flags)); 673   6 *this, buf, static_cast<int>(flags));
674   } 674   }
675   675  
676   /// @overload 676   /// @overload
677   template<capy::ConstBufferSequence Buffers> 677   template<capy::ConstBufferSequence Buffers>
HITCBC 678   6 auto send(Buffers const& buf) 678   6 auto send(Buffers const& buf)
679   { 679   {
HITCBC 680   6 return send(buf, corosio::message_flags::none); 680   6 return send(buf, corosio::message_flags::none);
681   } 681   }
682   682  
683   /** Receive a datagram from the connected peer. 683   /** Receive a datagram from the connected peer.
684   684  
685   @param buf The buffer to receive data into. 685   @param buf The buffer to receive data into.
686   @param flags Message flags (e.g. message_flags::peek). 686   @param flags Message flags (e.g. message_flags::peek).
687   687  
688   @return An awaitable that completes with 688   @return An awaitable that completes with
689   `io_result<std::size_t>`. 689   `io_result<std::size_t>`.
690   690  
691   @throws std::logic_error if the socket is not open. 691   @throws std::logic_error if the socket is not open.
692   */ 692   */
693   template<capy::MutableBufferSequence Buffers> 693   template<capy::MutableBufferSequence Buffers>
HITCBC 694   4 auto recv(Buffers const& buf, corosio::message_flags flags) 694   4 auto recv(Buffers const& buf, corosio::message_flags flags)
695   { 695   {
HITCBC 696   4 if (!is_open()) 696   4 if (!is_open())
MISUBC 697   detail::throw_logic_error("recv: socket not open"); 697   detail::throw_logic_error("recv: socket not open");
698   return recv_awaitable( 698   return recv_awaitable(
HITCBC 699   4 *this, buf, static_cast<int>(flags)); 699   4 *this, buf, static_cast<int>(flags));
700   } 700   }
701   701  
702   /// @overload 702   /// @overload
703   template<capy::MutableBufferSequence Buffers> 703   template<capy::MutableBufferSequence Buffers>
HITCBC 704   4 auto recv(Buffers const& buf) 704   4 auto recv(Buffers const& buf)
705   { 705   {
HITCBC 706   4 return recv(buf, corosio::message_flags::none); 706   4 return recv(buf, corosio::message_flags::none);
707   } 707   }
708   708  
709   /** Get the remote endpoint of the socket. 709   /** Get the remote endpoint of the socket.
710   710  
711   Returns the address and port of the connected peer. 711   Returns the address and port of the connected peer.
712   712  
713   @return The remote endpoint, or a default endpoint if 713   @return The remote endpoint, or a default endpoint if
714   not connected. 714   not connected.
715   */ 715   */
716   endpoint remote_endpoint() const noexcept; 716   endpoint remote_endpoint() const noexcept;
717   717  
718   protected: 718   protected:
719   /// Construct from a pre-built handle (for native_udp_socket). 719   /// Construct from a pre-built handle (for native_udp_socket).
720   explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h)) 720   explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
721   { 721   {
722   } 722   }
723   723  
724   private: 724   private:
725   /// Open the socket for the given protocol triple. 725   /// Open the socket for the given protocol triple.
726   void open_for_family(int family, int type, int protocol); 726   void open_for_family(int family, int type, int protocol);
727   727  
HITCBC 728   620 inline implementation& get() const noexcept 728   620 inline implementation& get() const noexcept
729   { 729   {
HITCBC 730   620 return *static_cast<implementation*>(h_.get()); 730   620 return *static_cast<implementation*>(h_.get());
731   } 731   }
732   }; 732   };
733   733  
734   } // namespace boost::corosio 734   } // namespace boost::corosio
735   735  
736   #endif // BOOST_COROSIO_UDP_SOCKET_HPP 736   #endif // BOOST_COROSIO_UDP_SOCKET_HPP