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