92.31% Lines (120/130) 100.00% Functions (37/37)
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_NATIVE_NATIVE_UDP_SOCKET_HPP 10   #ifndef BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
11   #define BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP 11   #define BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
12   12  
13   #include <boost/corosio/udp_socket.hpp> 13   #include <boost/corosio/udp_socket.hpp>
14   #include <boost/corosio/backend.hpp> 14   #include <boost/corosio/backend.hpp>
15   15  
16   #ifndef BOOST_COROSIO_MRDOCS 16   #ifndef BOOST_COROSIO_MRDOCS
17   #if BOOST_COROSIO_HAS_EPOLL 17   #if BOOST_COROSIO_HAS_EPOLL
18   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 18   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
19   #endif 19   #endif
20   20  
21   #if BOOST_COROSIO_HAS_SELECT 21   #if BOOST_COROSIO_HAS_SELECT
22   #include <boost/corosio/native/detail/select/select_types.hpp> 22   #include <boost/corosio/native/detail/select/select_types.hpp>
23   #endif 23   #endif
24   24  
25   #if BOOST_COROSIO_HAS_KQUEUE 25   #if BOOST_COROSIO_HAS_KQUEUE
26   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 26   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
27   #endif 27   #endif
28   28  
29   #if BOOST_COROSIO_HAS_IOCP 29   #if BOOST_COROSIO_HAS_IOCP
30   #include <boost/corosio/native/detail/iocp/win_udp_service.hpp> 30   #include <boost/corosio/native/detail/iocp/win_udp_service.hpp>
31   #endif 31   #endif
32   #endif // !BOOST_COROSIO_MRDOCS 32   #endif // !BOOST_COROSIO_MRDOCS
33   33  
34   namespace boost::corosio { 34   namespace boost::corosio {
35   35  
36   /** An asynchronous UDP socket with devirtualized I/O operations. 36   /** An asynchronous UDP socket with devirtualized I/O operations.
37   37  
38   This class template inherits from @ref udp_socket and shadows 38   This class template inherits from @ref udp_socket and shadows
39   the async operations (`send_to`, `recv_from`, `connect`, `send`, 39   the async operations (`send_to`, `recv_from`, `connect`, `send`,
40   `recv`) with versions that call the backend implementation 40   `recv`) with versions that call the backend implementation
41   directly, allowing the compiler to inline through the entire 41   directly, allowing the compiler to inline through the entire
42   call chain. 42   call chain.
43   43  
44   Non-async operations (`open`, `close`, `cancel`, `bind`, 44   Non-async operations (`open`, `close`, `cancel`, `bind`,
45   socket options) remain unchanged and dispatch through the 45   socket options) remain unchanged and dispatch through the
46   compiled library. 46   compiled library.
47   47  
48   A `native_udp_socket` IS-A `udp_socket` and can be passed to 48   A `native_udp_socket` IS-A `udp_socket` and can be passed to
49   any function expecting `udp_socket&`, in which case virtual 49   any function expecting `udp_socket&`, in which case virtual
50   dispatch is used transparently. 50   dispatch is used transparently.
51   51  
52   @tparam Backend A backend tag value (e.g., `epoll`) 52   @tparam Backend A backend tag value (e.g., `epoll`)
53   whose type provides the concrete implementation types. 53   whose type provides the concrete implementation types.
54   54  
55   @par Thread Safety 55   @par Thread Safety
56   Same as @ref udp_socket. 56   Same as @ref udp_socket.
57   57  
58   @par Example 58   @par Example
59   @code 59   @code
60   #include <boost/corosio/native/native_udp_socket.hpp> 60   #include <boost/corosio/native/native_udp_socket.hpp>
61   61  
62   native_io_context<epoll> ctx; 62   native_io_context<epoll> ctx;
63   native_udp_socket<epoll> s(ctx); 63   native_udp_socket<epoll> s(ctx);
64   s.open(); 64   s.open();
65   s.bind(endpoint(ipv4_address::any(), 9000)); 65   s.bind(endpoint(ipv4_address::any(), 9000));
66   char buf[1024]; 66   char buf[1024];
67   endpoint sender; 67   endpoint sender;
68   auto [ec, n] = co_await s.recv_from( 68   auto [ec, n] = co_await s.recv_from(
69   capy::mutable_buffer(buf, sizeof(buf)), sender); 69   capy::mutable_buffer(buf, sizeof(buf)), sender);
70   @endcode 70   @endcode
71   71  
72   @see udp_socket, epoll_t 72   @see udp_socket, epoll_t
73   */ 73   */
74   template<auto Backend> 74   template<auto Backend>
75   class native_udp_socket : public udp_socket 75   class native_udp_socket : public udp_socket
76   { 76   {
77   using backend_type = decltype(Backend); 77   using backend_type = decltype(Backend);
78   using impl_type = typename backend_type::udp_socket_type; 78   using impl_type = typename backend_type::udp_socket_type;
79   using service_type = typename backend_type::udp_service_type; 79   using service_type = typename backend_type::udp_service_type;
80   80  
HITGIC 81   impl_type& get_impl() noexcept 81   26 impl_type& get_impl() noexcept
82   { 82   {
HITGIC 83   return *static_cast<impl_type*>(h_.get()); 83   26 return *static_cast<impl_type*>(h_.get());
84   } 84   }
85   85  
86   template<class ConstBufferSequence> 86   template<class ConstBufferSequence>
87   struct native_send_to_awaitable 87   struct native_send_to_awaitable
88   { 88   {
89   native_udp_socket& self_; 89   native_udp_socket& self_;
90   ConstBufferSequence buffers_; 90   ConstBufferSequence buffers_;
91   endpoint dest_; 91   endpoint dest_;
92   int flags_; 92   int flags_;
93   std::stop_token token_; 93   std::stop_token token_;
94   mutable std::error_code ec_; 94   mutable std::error_code ec_;
95   mutable std::size_t bytes_transferred_ = 0; 95   mutable std::size_t bytes_transferred_ = 0;
96   96  
HITGIC 97   native_send_to_awaitable( 97   4 native_send_to_awaitable(
98   native_udp_socket& self, 98   native_udp_socket& self,
99   ConstBufferSequence buffers, 99   ConstBufferSequence buffers,
100   endpoint dest, 100   endpoint dest,
101   int flags) noexcept 101   int flags) noexcept
HITGIC 102   : self_(self) 102   4 : self_(self)
HITGIC 103   , buffers_(std::move(buffers)) 103   4 , buffers_(std::move(buffers))
HITGIC 104   , dest_(dest) 104   4 , dest_(dest)
HITGIC 105   , flags_(flags) 105   4 , flags_(flags)
106   { 106   {
HITGIC 107   } 107   4 }
108   108  
HITGIC 109   bool await_ready() const noexcept 109   4 bool await_ready() const noexcept
110   { 110   {
HITGIC 111   return token_.stop_requested(); 111   4 return token_.stop_requested();
112   } 112   }
113   113  
HITGIC 114   capy::io_result<std::size_t> await_resume() const noexcept 114   4 capy::io_result<std::size_t> await_resume() const noexcept
115   { 115   {
HITGIC 116   if (token_.stop_requested()) 116   4 if (token_.stop_requested())
MISUIC 117   return {make_error_code(std::errc::operation_canceled), 0}; 117   return {make_error_code(std::errc::operation_canceled), 0};
HITGIC 118   return {ec_, bytes_transferred_}; 118   4 return {ec_, bytes_transferred_};
119   } 119   }
120   120  
HITGIC 121   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 121   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
122   -> std::coroutine_handle<> 122   -> std::coroutine_handle<>
123   { 123   {
HITGIC 124   token_ = env->stop_token; 124   4 token_ = env->stop_token;
HITGIC 125   return self_.get_impl().send_to( 125   12 return self_.get_impl().send_to(
HITGIC 126   h, env->executor, buffers_, dest_, flags_, 126   4 h, env->executor, buffers_, dest_, flags_,
HITGIC 127   token_, &ec_, &bytes_transferred_); 127   12 token_, &ec_, &bytes_transferred_);
128   } 128   }
129   }; 129   };
130   130  
131   template<class MutableBufferSequence> 131   template<class MutableBufferSequence>
132   struct native_recv_from_awaitable 132   struct native_recv_from_awaitable
133   { 133   {
134   native_udp_socket& self_; 134   native_udp_socket& self_;
135   MutableBufferSequence buffers_; 135   MutableBufferSequence buffers_;
136   endpoint& source_; 136   endpoint& source_;
137   int flags_; 137   int flags_;
138   std::stop_token token_; 138   std::stop_token token_;
139   mutable std::error_code ec_; 139   mutable std::error_code ec_;
140   mutable std::size_t bytes_transferred_ = 0; 140   mutable std::size_t bytes_transferred_ = 0;
141   141  
HITGIC 142   native_recv_from_awaitable( 142   8 native_recv_from_awaitable(
143   native_udp_socket& self, 143   native_udp_socket& self,
144   MutableBufferSequence buffers, 144   MutableBufferSequence buffers,
145   endpoint& source, 145   endpoint& source,
146   int flags) noexcept 146   int flags) noexcept
HITGIC 147   : self_(self) 147   8 : self_(self)
HITGIC 148   , buffers_(std::move(buffers)) 148   8 , buffers_(std::move(buffers))
HITGIC 149   , source_(source) 149   8 , source_(source)
HITGIC 150   , flags_(flags) 150   8 , flags_(flags)
151   { 151   {
HITGIC 152   } 152   8 }
153   153  
HITGIC 154   bool await_ready() const noexcept 154   8 bool await_ready() const noexcept
155   { 155   {
HITGIC 156   return token_.stop_requested(); 156   8 return token_.stop_requested();
157   } 157   }
158   158  
HITGIC 159   capy::io_result<std::size_t> await_resume() const noexcept 159   8 capy::io_result<std::size_t> await_resume() const noexcept
160   { 160   {
HITGIC 161   if (token_.stop_requested()) 161   8 if (token_.stop_requested())
MISUIC 162   return {make_error_code(std::errc::operation_canceled), 0}; 162   return {make_error_code(std::errc::operation_canceled), 0};
HITGIC 163   return {ec_, bytes_transferred_}; 163   8 return {ec_, bytes_transferred_};
164   } 164   }
165   165  
HITGIC 166   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 166   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
167   -> std::coroutine_handle<> 167   -> std::coroutine_handle<>
168   { 168   {
HITGIC 169   token_ = env->stop_token; 169   8 token_ = env->stop_token;
HITGIC 170   return self_.get_impl().recv_from( 170   24 return self_.get_impl().recv_from(
HITGIC 171   h, env->executor, buffers_, &source_, flags_, 171   8 h, env->executor, buffers_, &source_, flags_,
HITGIC 172   token_, &ec_, &bytes_transferred_); 172   24 token_, &ec_, &bytes_transferred_);
173   } 173   }
174   }; 174   };
175   175  
176   struct native_wait_awaitable 176   struct native_wait_awaitable
177   { 177   {
178   native_udp_socket& self_; 178   native_udp_socket& self_;
179   wait_type w_; 179   wait_type w_;
180   std::stop_token token_; 180   std::stop_token token_;
181   mutable std::error_code ec_; 181   mutable std::error_code ec_;
182   182  
HITGIC 183   native_wait_awaitable(native_udp_socket& self, wait_type w) noexcept 183   2 native_wait_awaitable(native_udp_socket& self, wait_type w) noexcept
HITGIC 184   : self_(self) 184   2 : self_(self)
HITGIC 185   , w_(w) 185   2 , w_(w)
186   { 186   {
HITGIC 187   } 187   2 }
188   188  
HITGIC 189   bool await_ready() const noexcept 189   2 bool await_ready() const noexcept
190   { 190   {
HITGIC 191   return token_.stop_requested(); 191   2 return token_.stop_requested();
192   } 192   }
193   193  
HITGIC 194   capy::io_result<> await_resume() const noexcept 194   2 capy::io_result<> await_resume() const noexcept
195   { 195   {
HITGIC 196   if (token_.stop_requested()) 196   2 if (token_.stop_requested())
MISUIC 197   return {make_error_code(std::errc::operation_canceled)}; 197   return {make_error_code(std::errc::operation_canceled)};
HITGIC 198   return {ec_}; 198   2 return {ec_};
199   } 199   }
200   200  
HITGIC 201   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 201   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
202   -> std::coroutine_handle<> 202   -> std::coroutine_handle<>
203   { 203   {
HITGIC 204   token_ = env->stop_token; 204   2 token_ = env->stop_token;
HITGIC 205   return self_.get_impl().wait( 205   6 return self_.get_impl().wait(
HITGIC 206   h, env->executor, w_, token_, &ec_); 206   6 h, env->executor, w_, token_, &ec_);
207   } 207   }
208   }; 208   };
209   209  
210   struct native_connect_awaitable 210   struct native_connect_awaitable
211   { 211   {
212   native_udp_socket& self_; 212   native_udp_socket& self_;
213   endpoint endpoint_; 213   endpoint endpoint_;
214   std::stop_token token_; 214   std::stop_token token_;
215   mutable std::error_code ec_; 215   mutable std::error_code ec_;
216   216  
HITGIC 217   native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept 217   6 native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept
HITGIC 218   : self_(self) 218   6 : self_(self)
HITGIC 219   , endpoint_(ep) 219   6 , endpoint_(ep)
220   { 220   {
HITGIC 221   } 221   6 }
222   222  
HITGIC 223   bool await_ready() const noexcept 223   6 bool await_ready() const noexcept
224   { 224   {
HITGIC 225   return token_.stop_requested(); 225   6 return token_.stop_requested();
226   } 226   }
227   227  
HITGIC 228   capy::io_result<> await_resume() const noexcept 228   6 capy::io_result<> await_resume() const noexcept
229   { 229   {
HITGIC 230   if (token_.stop_requested()) 230   6 if (token_.stop_requested())
MISUIC 231   return {make_error_code(std::errc::operation_canceled)}; 231   return {make_error_code(std::errc::operation_canceled)};
HITGIC 232   return {ec_}; 232   6 return {ec_};
233   } 233   }
234   234  
HITGIC 235   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 235   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
236   -> std::coroutine_handle<> 236   -> std::coroutine_handle<>
237   { 237   {
HITGIC 238   token_ = env->stop_token; 238   6 token_ = env->stop_token;
HITGIC 239   return self_.get_impl().connect( 239   18 return self_.get_impl().connect(
HITGIC 240   h, env->executor, endpoint_, token_, &ec_); 240   18 h, env->executor, endpoint_, token_, &ec_);
241   } 241   }
242   }; 242   };
243   243  
244   template<class ConstBufferSequence> 244   template<class ConstBufferSequence>
245   struct native_send_awaitable 245   struct native_send_awaitable
246   { 246   {
247   native_udp_socket& self_; 247   native_udp_socket& self_;
248   ConstBufferSequence buffers_; 248   ConstBufferSequence buffers_;
249   int flags_; 249   int flags_;
250   std::stop_token token_; 250   std::stop_token token_;
251   mutable std::error_code ec_; 251   mutable std::error_code ec_;
252   mutable std::size_t bytes_transferred_ = 0; 252   mutable std::size_t bytes_transferred_ = 0;
253   253  
HITGIC 254   native_send_awaitable( 254   4 native_send_awaitable(
255   native_udp_socket& self, 255   native_udp_socket& self,
256   ConstBufferSequence buffers, 256   ConstBufferSequence buffers,
257   int flags) noexcept 257   int flags) noexcept
HITGIC 258   : self_(self) 258   4 : self_(self)
HITGIC 259   , buffers_(std::move(buffers)) 259   4 , buffers_(std::move(buffers))
HITGIC 260   , flags_(flags) 260   4 , flags_(flags)
261   { 261   {
HITGIC 262   } 262   4 }
263   263  
HITGIC 264   bool await_ready() const noexcept 264   4 bool await_ready() const noexcept
265   { 265   {
HITGIC 266   return token_.stop_requested(); 266   4 return token_.stop_requested();
267   } 267   }
268   268  
HITGIC 269   capy::io_result<std::size_t> await_resume() const noexcept 269   4 capy::io_result<std::size_t> await_resume() const noexcept
270   { 270   {
HITGIC 271   if (token_.stop_requested()) 271   4 if (token_.stop_requested())
MISUIC 272   return {make_error_code(std::errc::operation_canceled), 0}; 272   return {make_error_code(std::errc::operation_canceled), 0};
HITGIC 273   return {ec_, bytes_transferred_}; 273   4 return {ec_, bytes_transferred_};
274   } 274   }
275   275  
HITGIC 276   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 276   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
277   -> std::coroutine_handle<> 277   -> std::coroutine_handle<>
278   { 278   {
HITGIC 279   token_ = env->stop_token; 279   4 token_ = env->stop_token;
HITGIC 280   return self_.get_impl().send( 280   12 return self_.get_impl().send(
HITGIC 281   h, env->executor, buffers_, flags_, 281   4 h, env->executor, buffers_, flags_,
HITGIC 282   token_, &ec_, &bytes_transferred_); 282   12 token_, &ec_, &bytes_transferred_);
283   } 283   }
284   }; 284   };
285   285  
286   template<class MutableBufferSequence> 286   template<class MutableBufferSequence>
287   struct native_recv_awaitable 287   struct native_recv_awaitable
288   { 288   {
289   native_udp_socket& self_; 289   native_udp_socket& self_;
290   MutableBufferSequence buffers_; 290   MutableBufferSequence buffers_;
291   int flags_; 291   int flags_;
292   std::stop_token token_; 292   std::stop_token token_;
293   mutable std::error_code ec_; 293   mutable std::error_code ec_;
294   mutable std::size_t bytes_transferred_ = 0; 294   mutable std::size_t bytes_transferred_ = 0;
295   295  
HITGIC 296   native_recv_awaitable( 296   2 native_recv_awaitable(
297   native_udp_socket& self, 297   native_udp_socket& self,
298   MutableBufferSequence buffers, 298   MutableBufferSequence buffers,
299   int flags) noexcept 299   int flags) noexcept
HITGIC 300   : self_(self) 300   2 : self_(self)
HITGIC 301   , buffers_(std::move(buffers)) 301   2 , buffers_(std::move(buffers))
HITGIC 302   , flags_(flags) 302   2 , flags_(flags)
303   { 303   {
HITGIC 304   } 304   2 }
305   305  
HITGIC 306   bool await_ready() const noexcept 306   2 bool await_ready() const noexcept
307   { 307   {
HITGIC 308   return token_.stop_requested(); 308   2 return token_.stop_requested();
309   } 309   }
310   310  
HITGIC 311   capy::io_result<std::size_t> await_resume() const noexcept 311   2 capy::io_result<std::size_t> await_resume() const noexcept
312   { 312   {
HITGIC 313   if (token_.stop_requested()) 313   2 if (token_.stop_requested())
MISUIC 314   return {make_error_code(std::errc::operation_canceled), 0}; 314   return {make_error_code(std::errc::operation_canceled), 0};
HITGIC 315   return {ec_, bytes_transferred_}; 315   2 return {ec_, bytes_transferred_};
316   } 316   }
317   317  
HITGIC 318   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 318   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
319   -> std::coroutine_handle<> 319   -> std::coroutine_handle<>
320   { 320   {
HITGIC 321   token_ = env->stop_token; 321   2 token_ = env->stop_token;
HITGIC 322   return self_.get_impl().recv( 322   6 return self_.get_impl().recv(
HITGIC 323   h, env->executor, buffers_, flags_, 323   2 h, env->executor, buffers_, flags_,
HITGIC 324   token_, &ec_, &bytes_transferred_); 324   6 token_, &ec_, &bytes_transferred_);
325   } 325   }
326   }; 326   };
327   327  
328   public: 328   public:
329   /** Construct a native UDP socket from an execution context. 329   /** Construct a native UDP socket from an execution context.
330   330  
331   @param ctx The execution context that will own this socket. 331   @param ctx The execution context that will own this socket.
332   */ 332   */
HITGIC 333   explicit native_udp_socket(capy::execution_context& ctx) 333   36 explicit native_udp_socket(capy::execution_context& ctx)
HITGIC 334   : udp_socket(create_handle<service_type>(ctx)) 334   36 : udp_socket(create_handle<service_type>(ctx))
335   { 335   {
HITGIC 336   } 336   36 }
337   337  
338   /** Construct a native UDP socket from an executor. 338   /** Construct a native UDP socket from an executor.
339   339  
340   @param ex The executor whose context will own the socket. 340   @param ex The executor whose context will own the socket.
341   */ 341   */
342   template<class Ex> 342   template<class Ex>
343   requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) && 343   requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) &&
344   capy::Executor<Ex> 344   capy::Executor<Ex>
345   explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context()) 345   explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context())
346   { 346   {
347   } 347   }
348   348  
349   /// Move construct. 349   /// Move construct.
HITGIC 350   native_udp_socket(native_udp_socket&&) noexcept = default; 350   2 native_udp_socket(native_udp_socket&&) noexcept = default;
351   351  
352   /// Move assign. 352   /// Move assign.
353   native_udp_socket& operator=(native_udp_socket&&) noexcept = default; 353   native_udp_socket& operator=(native_udp_socket&&) noexcept = default;
354   354  
355   native_udp_socket(native_udp_socket const&) = delete; 355   native_udp_socket(native_udp_socket const&) = delete;
356   native_udp_socket& operator=(native_udp_socket const&) = delete; 356   native_udp_socket& operator=(native_udp_socket const&) = delete;
357   357  
358   /** Send a datagram to the specified destination. 358   /** Send a datagram to the specified destination.
359   359  
360   Calls the backend implementation directly, bypassing virtual 360   Calls the backend implementation directly, bypassing virtual
361   dispatch. Otherwise identical to @ref udp_socket::send_to. 361   dispatch. Otherwise identical to @ref udp_socket::send_to.
362   362  
363   @param buffers The buffer sequence containing data to send. 363   @param buffers The buffer sequence containing data to send.
364   @param dest The destination endpoint. 364   @param dest The destination endpoint.
365   @param flags Message flags. 365   @param flags Message flags.
366   366  
367   @return An awaitable yielding `(error_code, std::size_t)`. 367   @return An awaitable yielding `(error_code, std::size_t)`.
368   */ 368   */
369   template<capy::ConstBufferSequence CB> 369   template<capy::ConstBufferSequence CB>
HITGIC 370   auto send_to( 370   4 auto send_to(
371   CB const& buffers, 371   CB const& buffers,
372   endpoint dest, 372   endpoint dest,
373   corosio::message_flags flags) 373   corosio::message_flags flags)
374   { 374   {
HITGIC 375   if (!is_open()) 375   4 if (!is_open())
MISUIC 376   detail::throw_logic_error("send_to: socket not open"); 376   detail::throw_logic_error("send_to: socket not open");
377   return native_send_to_awaitable<CB>( 377   return native_send_to_awaitable<CB>(
HITGIC 378   *this, buffers, dest, static_cast<int>(flags)); 378   4 *this, buffers, dest, static_cast<int>(flags));
379   } 379   }
380   380  
381   /// @overload 381   /// @overload
382   template<capy::ConstBufferSequence CB> 382   template<capy::ConstBufferSequence CB>
HITGIC 383   auto send_to(CB const& buffers, endpoint dest) 383   4 auto send_to(CB const& buffers, endpoint dest)
384   { 384   {
HITGIC 385   return send_to(buffers, dest, corosio::message_flags::none); 385   4 return send_to(buffers, dest, corosio::message_flags::none);
386   } 386   }
387   387  
388   /** Receive a datagram and capture the sender's endpoint. 388   /** Receive a datagram and capture the sender's endpoint.
389   389  
390   Calls the backend implementation directly, bypassing virtual 390   Calls the backend implementation directly, bypassing virtual
391   dispatch. Otherwise identical to @ref udp_socket::recv_from. 391   dispatch. Otherwise identical to @ref udp_socket::recv_from.
392   392  
393   @param buffers The buffer sequence to receive data into. 393   @param buffers The buffer sequence to receive data into.
394   @param source Reference to an endpoint that will be set to 394   @param source Reference to an endpoint that will be set to
395   the sender's address on successful completion. 395   the sender's address on successful completion.
396   @param flags Message flags (e.g. message_flags::peek). 396   @param flags Message flags (e.g. message_flags::peek).
397   397  
398   @return An awaitable yielding `(error_code, std::size_t)`. 398   @return An awaitable yielding `(error_code, std::size_t)`.
399   */ 399   */
400   template<capy::MutableBufferSequence MB> 400   template<capy::MutableBufferSequence MB>
HITGIC 401   auto recv_from( 401   8 auto recv_from(
402   MB const& buffers, 402   MB const& buffers,
403   endpoint& source, 403   endpoint& source,
404   corosio::message_flags flags) 404   corosio::message_flags flags)
405   { 405   {
HITGIC 406   if (!is_open()) 406   8 if (!is_open())
MISUIC 407   detail::throw_logic_error("recv_from: socket not open"); 407   detail::throw_logic_error("recv_from: socket not open");
408   return native_recv_from_awaitable<MB>( 408   return native_recv_from_awaitable<MB>(
HITGIC 409   *this, buffers, source, static_cast<int>(flags)); 409   8 *this, buffers, source, static_cast<int>(flags));
410   } 410   }
411   411  
412   /// @overload 412   /// @overload
413   template<capy::MutableBufferSequence MB> 413   template<capy::MutableBufferSequence MB>
HITGIC 414   auto recv_from(MB const& buffers, endpoint& source) 414   8 auto recv_from(MB const& buffers, endpoint& source)
415   { 415   {
HITGIC 416   return recv_from(buffers, source, corosio::message_flags::none); 416   8 return recv_from(buffers, source, corosio::message_flags::none);
417   } 417   }
418   418  
419   /** Asynchronously connect to set the default peer. 419   /** Asynchronously connect to set the default peer.
420   420  
421   Calls the backend implementation directly, bypassing virtual 421   Calls the backend implementation directly, bypassing virtual
422   dispatch. Otherwise identical to @ref udp_socket::connect. 422   dispatch. Otherwise identical to @ref udp_socket::connect.
423   423  
424   If the socket is not already open, it is opened automatically 424   If the socket is not already open, it is opened automatically
425   using the address family of @p ep. 425   using the address family of @p ep.
426   426  
427   @param ep The remote endpoint to connect to. 427   @param ep The remote endpoint to connect to.
428   428  
429   @return An awaitable yielding `io_result<>`. 429   @return An awaitable yielding `io_result<>`.
430   430  
431   @throws std::system_error if the socket needs to be opened 431   @throws std::system_error if the socket needs to be opened
432   and the open fails. 432   and the open fails.
433   */ 433   */
HITGIC 434   auto connect(endpoint ep) 434   6 auto connect(endpoint ep)
435   { 435   {
HITGIC 436   if (!is_open()) 436   6 if (!is_open())
HITGIC 437   open(ep.is_v6() ? udp::v6() : udp::v4()); 437   4 open(ep.is_v6() ? udp::v6() : udp::v4());
HITGIC 438   return native_connect_awaitable(*this, ep); 438   6 return native_connect_awaitable(*this, ep);
439   } 439   }
440   440  
441   /** Send a datagram to the connected peer. 441   /** Send a datagram to the connected peer.
442   442  
443   Calls the backend implementation directly, bypassing virtual 443   Calls the backend implementation directly, bypassing virtual
444   dispatch. Otherwise identical to @ref udp_socket::send. 444   dispatch. Otherwise identical to @ref udp_socket::send.
445   445  
446   @param buffers The buffer sequence containing data to send. 446   @param buffers The buffer sequence containing data to send.
447   @param flags Message flags. 447   @param flags Message flags.
448   448  
449   @return An awaitable yielding `(error_code, std::size_t)`. 449   @return An awaitable yielding `(error_code, std::size_t)`.
450   450  
451   @throws std::logic_error if the socket is not open. 451   @throws std::logic_error if the socket is not open.
452   */ 452   */
453   template<capy::ConstBufferSequence CB> 453   template<capy::ConstBufferSequence CB>
HITGIC 454   auto send(CB const& buffers, corosio::message_flags flags) 454   4 auto send(CB const& buffers, corosio::message_flags flags)
455   { 455   {
HITGIC 456   if (!is_open()) 456   4 if (!is_open())
MISUIC 457   detail::throw_logic_error("send: socket not open"); 457   detail::throw_logic_error("send: socket not open");
458   return native_send_awaitable<CB>( 458   return native_send_awaitable<CB>(
HITGIC 459   *this, buffers, static_cast<int>(flags)); 459   4 *this, buffers, static_cast<int>(flags));
460   } 460   }
461   461  
462   /// @overload 462   /// @overload
463   template<capy::ConstBufferSequence CB> 463   template<capy::ConstBufferSequence CB>
HITGIC 464   auto send(CB const& buffers) 464   4 auto send(CB const& buffers)
465   { 465   {
HITGIC 466   return send(buffers, corosio::message_flags::none); 466   4 return send(buffers, corosio::message_flags::none);
467   } 467   }
468   468  
469   /** Receive a datagram from the connected peer. 469   /** Receive a datagram from the connected peer.
470   470  
471   Calls the backend implementation directly, bypassing virtual 471   Calls the backend implementation directly, bypassing virtual
472   dispatch. Otherwise identical to @ref udp_socket::recv. 472   dispatch. Otherwise identical to @ref udp_socket::recv.
473   473  
474   @param buffers The buffer sequence to receive data into. 474   @param buffers The buffer sequence to receive data into.
475   @param flags Message flags (e.g. message_flags::peek). 475   @param flags Message flags (e.g. message_flags::peek).
476   476  
477   @return An awaitable yielding `(error_code, std::size_t)`. 477   @return An awaitable yielding `(error_code, std::size_t)`.
478   478  
479   @throws std::logic_error if the socket is not open. 479   @throws std::logic_error if the socket is not open.
480   */ 480   */
481   template<capy::MutableBufferSequence MB> 481   template<capy::MutableBufferSequence MB>
HITGIC 482   auto recv(MB const& buffers, corosio::message_flags flags) 482   2 auto recv(MB const& buffers, corosio::message_flags flags)
483   { 483   {
HITGIC 484   if (!is_open()) 484   2 if (!is_open())
MISUIC 485   detail::throw_logic_error("recv: socket not open"); 485   detail::throw_logic_error("recv: socket not open");
486   return native_recv_awaitable<MB>( 486   return native_recv_awaitable<MB>(
HITGIC 487   *this, buffers, static_cast<int>(flags)); 487   2 *this, buffers, static_cast<int>(flags));
488   } 488   }
489   489  
490   /// @overload 490   /// @overload
491   template<capy::MutableBufferSequence MB> 491   template<capy::MutableBufferSequence MB>
HITGIC 492   auto recv(MB const& buffers) 492   2 auto recv(MB const& buffers)
493   { 493   {
HITGIC 494   return recv(buffers, corosio::message_flags::none); 494   2 return recv(buffers, corosio::message_flags::none);
495   } 495   }
496   496  
497   /** Asynchronously wait for the socket to be ready. 497   /** Asynchronously wait for the socket to be ready.
498   498  
499   Calls the backend implementation directly, bypassing virtual 499   Calls the backend implementation directly, bypassing virtual
500   dispatch. Otherwise identical to @ref udp_socket::wait. 500   dispatch. Otherwise identical to @ref udp_socket::wait.
501   501  
502   @param w The wait direction (read, write, or error). 502   @param w The wait direction (read, write, or error).
503   503  
504   @return An awaitable yielding `io_result<>`. 504   @return An awaitable yielding `io_result<>`.
505   */ 505   */
HITGIC 506   [[nodiscard]] auto wait(wait_type w) 506   2 [[nodiscard]] auto wait(wait_type w)
507   { 507   {
HITGIC 508   return native_wait_awaitable(*this, w); 508   2 return native_wait_awaitable(*this, w);
509   } 509   }
510   }; 510   };
511   511  
512   } // namespace boost::corosio 512   } // namespace boost::corosio
513   513  
514   #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP 514   #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP