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