100.00% Lines (134/134) 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_IO_URING 30 + #if BOOST_COROSIO_HAS_URING
31 - #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp> 31 + #include <boost/corosio/native/detail/uring/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_, 122 + 6 h, env->executor, buffers_, dest_, flags_, token_, &ec_,
HITCBC 123 - 18 token_, &ec_, &bytes_transferred_); 123 + 12 &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_, 169 + 10 h, env->executor, buffers_, &source_, flags_, token_, &ec_,
HITCBC 170 - 30 token_, &ec_, &bytes_transferred_); 170 + 20 &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 - 12 return self_.get_impl().wait( 205 + 4 return self_.get_impl().wait(h, env->executor, w_, token_, &ec_);
DCB 206 - 12 h, env->executor, w_, token_, &ec_);  
207   } 206   }
208   }; 207   };
209   208  
210   struct native_connect_awaitable 209   struct native_connect_awaitable
211   { 210   {
212   native_udp_socket& self_; 211   native_udp_socket& self_;
213   endpoint endpoint_; 212   endpoint endpoint_;
214   std::stop_token token_; 213   std::stop_token token_;
215   mutable std::error_code ec_; 214   mutable std::error_code ec_;
216   215  
HITCBC 217   10 native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept 216   10 native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept
HITCBC 218   10 : self_(self) 217   10 : self_(self)
HITCBC 219   10 , endpoint_(ep) 218   10 , endpoint_(ep)
220   { 219   {
HITCBC 221   10 } 220   10 }
222   221  
HITCBC 223   10 bool await_ready() const noexcept 222   10 bool await_ready() const noexcept
224   { 223   {
225   // A pre-set ec_ means the initiator failed before 224   // A pre-set ec_ means the initiator failed before
226   // dispatch (e.g. a closed object). 225   // dispatch (e.g. a closed object).
HITCBC 227   10 return static_cast<bool>(ec_) || token_.stop_requested(); 226   10 return static_cast<bool>(ec_) || token_.stop_requested();
228   } 227   }
229   228  
HITCBC 230   10 [[nodiscard]] capy::io_result<> await_resume() const noexcept 229   10 [[nodiscard]] capy::io_result<> await_resume() const noexcept
231   { 230   {
HITCBC 232   10 if (token_.stop_requested()) 231   10 if (token_.stop_requested())
HITCBC 233   2 return {make_error_code(std::errc::operation_canceled)}; 232   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 234   8 return {ec_}; 233   8 return {ec_};
235   } 234   }
236   235  
HITCBC 237   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 236   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
238   -> std::coroutine_handle<> 237   -> std::coroutine_handle<>
239   { 238   {
HITCBC 240   10 token_ = env->stop_token; 239   10 token_ = env->stop_token;
HITCBC 241   30 return self_.get_impl().connect( 240   30 return self_.get_impl().connect(
HITCBC 242   30 h, env->executor, endpoint_, token_, &ec_); 241   30 h, env->executor, endpoint_, token_, &ec_);
243   } 242   }
244   }; 243   };
245   244  
246   template<class ConstBufferSequence> 245   template<class ConstBufferSequence>
247   struct native_send_awaitable 246   struct native_send_awaitable
248   { 247   {
249   native_udp_socket& self_; 248   native_udp_socket& self_;
250   ConstBufferSequence buffers_; 249   ConstBufferSequence buffers_;
251   int flags_; 250   int flags_;
252   std::stop_token token_; 251   std::stop_token token_;
253   mutable std::error_code ec_; 252   mutable std::error_code ec_;
254   mutable std::size_t bytes_transferred_ = 0; 253   mutable std::size_t bytes_transferred_ = 0;
255   254  
HITCBC 256   8 native_send_awaitable( 255   8 native_send_awaitable(
257   native_udp_socket& self, 256   native_udp_socket& self,
258   ConstBufferSequence buffers, 257   ConstBufferSequence buffers,
259   int flags) noexcept 258   int flags) noexcept
HITCBC 260   8 : self_(self) 259   8 : self_(self)
HITCBC 261   8 , buffers_(std::move(buffers)) 260   8 , buffers_(std::move(buffers))
HITCBC 262   8 , flags_(flags) 261   8 , flags_(flags)
263   { 262   {
HITCBC 264   8 } 263   8 }
265   264  
HITCBC 266   8 bool await_ready() const noexcept 265   8 bool await_ready() const noexcept
267   { 266   {
268   // A pre-set ec_ means the initiator failed before 267   // A pre-set ec_ means the initiator failed before
269   // dispatch (e.g. a closed object). 268   // dispatch (e.g. a closed object).
HITCBC 270   8 return static_cast<bool>(ec_) || token_.stop_requested(); 269   8 return static_cast<bool>(ec_) || token_.stop_requested();
271   } 270   }
272   271  
HITCBC 273   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 272   8 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
274   { 273   {
HITCBC 275   8 if (token_.stop_requested()) 274   8 if (token_.stop_requested())
HITCBC 276   2 return {make_error_code(std::errc::operation_canceled), 0}; 275   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 277   6 return {ec_, bytes_transferred_}; 276   6 return {ec_, bytes_transferred_};
278   } 277   }
279   278  
HITCBC 280   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 279   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
281   -> std::coroutine_handle<> 280   -> std::coroutine_handle<>
282   { 281   {
HITCBC 283   6 token_ = env->stop_token; 282   6 token_ = env->stop_token;
HITCBC 284   18 return self_.get_impl().send( 283   18 return self_.get_impl().send(
HITCBC 285 - 6 h, env->executor, buffers_, flags_, 284 + 6 h, env->executor, buffers_, flags_, token_, &ec_,
HITCBC 286 - 18 token_, &ec_, &bytes_transferred_); 285 + 12 &bytes_transferred_);
287   } 286   }
288   }; 287   };
289   288  
290   template<class MutableBufferSequence> 289   template<class MutableBufferSequence>
291   struct native_recv_awaitable 290   struct native_recv_awaitable
292   { 291   {
293   native_udp_socket& self_; 292   native_udp_socket& self_;
294   MutableBufferSequence buffers_; 293   MutableBufferSequence buffers_;
295   int flags_; 294   int flags_;
296   std::stop_token token_; 295   std::stop_token token_;
297   mutable std::error_code ec_; 296   mutable std::error_code ec_;
298   mutable std::size_t bytes_transferred_ = 0; 297   mutable std::size_t bytes_transferred_ = 0;
299   298  
HITCBC 300   6 native_recv_awaitable( 299   6 native_recv_awaitable(
301   native_udp_socket& self, 300   native_udp_socket& self,
302   MutableBufferSequence buffers, 301   MutableBufferSequence buffers,
303   int flags) noexcept 302   int flags) noexcept
HITCBC 304   6 : self_(self) 303   6 : self_(self)
HITCBC 305   6 , buffers_(std::move(buffers)) 304   6 , buffers_(std::move(buffers))
HITCBC 306   6 , flags_(flags) 305   6 , flags_(flags)
307   { 306   {
HITCBC 308   6 } 307   6 }
309   308  
HITCBC 310   6 bool await_ready() const noexcept 309   6 bool await_ready() const noexcept
311   { 310   {
312   // A pre-set ec_ means the initiator failed before 311   // A pre-set ec_ means the initiator failed before
313   // dispatch (e.g. a closed object). 312   // dispatch (e.g. a closed object).
HITCBC 314   6 return static_cast<bool>(ec_) || token_.stop_requested(); 313   6 return static_cast<bool>(ec_) || token_.stop_requested();
315   } 314   }
316   315  
HITCBC 317   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 316   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
318   { 317   {
HITCBC 319   6 if (token_.stop_requested()) 318   6 if (token_.stop_requested())
HITCBC 320   2 return {make_error_code(std::errc::operation_canceled), 0}; 319   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 321   4 return {ec_, bytes_transferred_}; 320   4 return {ec_, bytes_transferred_};
322   } 321   }
323   322  
HITCBC 324   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 323   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
325   -> std::coroutine_handle<> 324   -> std::coroutine_handle<>
326   { 325   {
HITCBC 327   4 token_ = env->stop_token; 326   4 token_ = env->stop_token;
HITCBC 328   12 return self_.get_impl().recv( 327   12 return self_.get_impl().recv(
HITCBC 329 - 4 h, env->executor, buffers_, flags_, 328 + 4 h, env->executor, buffers_, flags_, token_, &ec_,
HITCBC 330 - 12 token_, &ec_, &bytes_transferred_); 329 + 8 &bytes_transferred_);
331   } 330   }
332   }; 331   };
333   332  
334   public: 333   public:
335   /** Construct a native UDP socket from an execution context. 334   /** Construct a native UDP socket from an execution context.
336   335  
337   @param ctx The execution context that will own this socket. 336   @param ctx The execution context that will own this socket.
338   */ 337   */
HITCBC 339   42 explicit native_udp_socket(capy::execution_context& ctx) 338   42 explicit native_udp_socket(capy::execution_context& ctx)
HITCBC 340   42 : udp_socket(create_handle<service_type>(ctx)) 339   42 : udp_socket(create_handle<service_type>(ctx))
341   { 340   {
HITCBC 342   42 } 341   42 }
343   342  
344   /** Construct a native UDP socket from an executor. 343   /** Construct a native UDP socket from an executor.
345   344  
346   @param ex The executor whose context will own the socket. 345   @param ex The executor whose context will own the socket.
347   */ 346   */
348   template<class Ex> 347   template<class Ex>
349   requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) && 348   requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) &&
350   capy::Executor<Ex> 349   capy::Executor<Ex>
351   explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context()) 350   explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context())
352   { 351   {
353   } 352   }
354   353  
355   /// Move construct. 354   /// Move construct.
HITCBC 356   2 native_udp_socket(native_udp_socket&&) noexcept = default; 355   2 native_udp_socket(native_udp_socket&&) noexcept = default;
357   356  
358   /// Move assign. 357   /// Move assign.
359   native_udp_socket& operator=(native_udp_socket&&) noexcept = default; 358   native_udp_socket& operator=(native_udp_socket&&) noexcept = default;
360   359  
361   native_udp_socket(native_udp_socket const&) = delete; 360   native_udp_socket(native_udp_socket const&) = delete;
362   native_udp_socket& operator=(native_udp_socket const&) = delete; 361   native_udp_socket& operator=(native_udp_socket const&) = delete;
363   362  
364   /** Send a datagram to the specified destination. 363   /** Send a datagram to the specified destination.
365   364  
366   Calls the backend implementation directly, bypassing virtual 365   Calls the backend implementation directly, bypassing virtual
367   dispatch. Otherwise identical to @ref udp_socket::send_to. 366   dispatch. Otherwise identical to @ref udp_socket::send_to.
368   367  
369   @param buffers The buffer sequence containing data to send. 368   @param buffers The buffer sequence containing data to send.
370   @param dest The destination endpoint. 369   @param dest The destination endpoint.
371   @param flags Message flags. 370   @param flags Message flags.
372   371  
373   @return An awaitable yielding `(error_code, std::size_t)`. 372   @return An awaitable yielding `(error_code, std::size_t)`.
374   373  
375   A closed socket reports `errc::bad_file_descriptor`. 374   A closed socket reports `errc::bad_file_descriptor`.
376   */ 375   */
377   template<capy::ConstBufferSequence CB> 376   template<capy::ConstBufferSequence CB>
ECB 378 - 8 [[nodiscard]] auto send_to( 377 + [[nodiscard]] auto
HITGIC 379 - CB const& buffers, 378 + 8 send_to(CB const& buffers, endpoint dest, corosio::message_flags flags)
380 - endpoint dest,  
381 - corosio::message_flags flags)  
382   { 379   {
HITCBC 383 - 8 native_send_to_awaitable<CB> aw(*this, buffers, dest, static_cast<int>(flags)); 380 + 8 native_send_to_awaitable<CB> aw(
  381 + *this, buffers, dest, static_cast<int>(flags));
HITCBC 384   8 if (!is_open()) 382   8 if (!is_open())
HITCBC 385   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 383   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 386   8 return aw; 384   8 return aw;
387   } 385   }
388   386  
389   /// @overload 387   /// @overload
390   template<capy::ConstBufferSequence CB> 388   template<capy::ConstBufferSequence CB>
HITCBC 391   8 [[nodiscard]] auto send_to(CB const& buffers, endpoint dest) 389   8 [[nodiscard]] auto send_to(CB const& buffers, endpoint dest)
392   { 390   {
HITCBC 393   8 return send_to(buffers, dest, corosio::message_flags::none); 391   8 return send_to(buffers, dest, corosio::message_flags::none);
394   } 392   }
395   393  
396   /** Receive a datagram and capture the sender's endpoint. 394   /** Receive a datagram and capture the sender's endpoint.
397   395  
398   Calls the backend implementation directly, bypassing virtual 396   Calls the backend implementation directly, bypassing virtual
399   dispatch. Otherwise identical to @ref udp_socket::recv_from. 397   dispatch. Otherwise identical to @ref udp_socket::recv_from.
400   398  
401   @param buffers The buffer sequence to receive data into. 399   @param buffers The buffer sequence to receive data into.
402   @param source Reference to an endpoint that will be set to 400   @param source Reference to an endpoint that will be set to
403   the sender's address on successful completion. 401   the sender's address on successful completion.
404   @param flags Message flags (e.g. message_flags::peek). 402   @param flags Message flags (e.g. message_flags::peek).
405   403  
406   @return An awaitable yielding `(error_code, std::size_t)`. 404   @return An awaitable yielding `(error_code, std::size_t)`.
407   405  
408   A closed socket reports `errc::bad_file_descriptor`. 406   A closed socket reports `errc::bad_file_descriptor`.
409   */ 407   */
410   template<capy::MutableBufferSequence MB> 408   template<capy::MutableBufferSequence MB>
ECB 411 - 12 [[nodiscard]] auto recv_from( 409 + [[nodiscard]] auto
HITGIC 412 - MB const& buffers, 410 + 12 recv_from(MB const& buffers, endpoint& source, corosio::message_flags flags)
413 - endpoint& source,  
414 - corosio::message_flags flags)  
415   { 411   {
HITCBC 416 - 12 native_recv_from_awaitable<MB> aw(*this, buffers, source, static_cast<int>(flags)); 412 + 12 native_recv_from_awaitable<MB> aw(
  413 + *this, buffers, source, static_cast<int>(flags));
HITCBC 417   12 if (!is_open()) 414   12 if (!is_open())
HITCBC 418   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 415   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 419   12 return aw; 416   12 return aw;
420   } 417   }
421   418  
422   /// @overload 419   /// @overload
423   template<capy::MutableBufferSequence MB> 420   template<capy::MutableBufferSequence MB>
HITCBC 424   12 [[nodiscard]] auto recv_from(MB const& buffers, endpoint& source) 421   12 [[nodiscard]] auto recv_from(MB const& buffers, endpoint& source)
425   { 422   {
HITCBC 426   12 return recv_from(buffers, source, corosio::message_flags::none); 423   12 return recv_from(buffers, source, corosio::message_flags::none);
427   } 424   }
428   425  
429   /** Asynchronously connect to set the default peer. 426   /** Asynchronously connect to set the default peer.
430   427  
431   Calls the backend implementation directly, bypassing virtual 428   Calls the backend implementation directly, bypassing virtual
432   dispatch. Otherwise identical to @ref udp_socket::connect. 429   dispatch. Otherwise identical to @ref udp_socket::connect.
433   430  
434   If the socket is not already open, it is opened automatically 431   If the socket is not already open, it is opened automatically
435   using the address family of @p ep. 432   using the address family of @p ep.
436   433  
437   @param ep The remote endpoint to connect to. 434   @param ep The remote endpoint to connect to.
438   435  
439   @return An awaitable yielding `io_result<>`. 436   @return An awaitable yielding `io_result<>`.
440   437  
441   If the socket needs to be opened and the open fails, the 438   If the socket needs to be opened and the open fails, the
442   awaitable completes immediately with that error. 439   awaitable completes immediately with that error.
443   */ 440   */
HITCBC 444   10 [[nodiscard]] auto connect(endpoint ep) 441   10 [[nodiscard]] auto connect(endpoint ep)
445   { 442   {
HITCBC 446   10 native_connect_awaitable aw(*this, ep); 443   10 native_connect_awaitable aw(*this, ep);
HITCBC 447   10 if (!is_open()) 444   10 if (!is_open())
HITCBC 448   4 aw.ec_ = open(ep.is_v6() ? udp::v6() : udp::v4()); 445   4 aw.ec_ = open(ep.is_v6() ? udp::v6() : udp::v4());
HITCBC 449   10 return aw; 446   10 return aw;
450   } 447   }
451   448  
452   /** Send a datagram to the connected peer. 449   /** Send a datagram to the connected peer.
453   450  
454   Calls the backend implementation directly, bypassing virtual 451   Calls the backend implementation directly, bypassing virtual
455   dispatch. Otherwise identical to @ref udp_socket::send. 452   dispatch. Otherwise identical to @ref udp_socket::send.
456   453  
457   @param buffers The buffer sequence containing data to send. 454   @param buffers The buffer sequence containing data to send.
458   @param flags Message flags. 455   @param flags Message flags.
459   456  
460   @return An awaitable yielding `(error_code, std::size_t)`. 457   @return An awaitable yielding `(error_code, std::size_t)`.
461   458  
462   A closed socket reports `errc::bad_file_descriptor`. 459   A closed socket reports `errc::bad_file_descriptor`.
463   */ 460   */
464   template<capy::ConstBufferSequence CB> 461   template<capy::ConstBufferSequence CB>
HITCBC 465   8 [[nodiscard]] auto send(CB const& buffers, corosio::message_flags flags) 462   8 [[nodiscard]] auto send(CB const& buffers, corosio::message_flags flags)
466   { 463   {
HITCBC 467   8 native_send_awaitable<CB> aw(*this, buffers, static_cast<int>(flags)); 464   8 native_send_awaitable<CB> aw(*this, buffers, static_cast<int>(flags));
HITCBC 468   8 if (!is_open()) 465   8 if (!is_open())
HITCBC 469   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 466   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 470   8 return aw; 467   8 return aw;
471   } 468   }
472   469  
473   /// @overload 470   /// @overload
474   template<capy::ConstBufferSequence CB> 471   template<capy::ConstBufferSequence CB>
HITCBC 475   8 [[nodiscard]] auto send(CB const& buffers) 472   8 [[nodiscard]] auto send(CB const& buffers)
476   { 473   {
HITCBC 477   8 return send(buffers, corosio::message_flags::none); 474   8 return send(buffers, corosio::message_flags::none);
478   } 475   }
479   476  
480   /** Receive a datagram from the connected peer. 477   /** Receive a datagram from the connected peer.
481   478  
482   Calls the backend implementation directly, bypassing virtual 479   Calls the backend implementation directly, bypassing virtual
483   dispatch. Otherwise identical to @ref udp_socket::recv. 480   dispatch. Otherwise identical to @ref udp_socket::recv.
484   481  
485   @param buffers The buffer sequence to receive data into. 482   @param buffers The buffer sequence to receive data into.
486   @param flags Message flags (e.g. message_flags::peek). 483   @param flags Message flags (e.g. message_flags::peek).
487   484  
488   @return An awaitable yielding `(error_code, std::size_t)`. 485   @return An awaitable yielding `(error_code, std::size_t)`.
489   486  
490   A closed socket reports `errc::bad_file_descriptor`. 487   A closed socket reports `errc::bad_file_descriptor`.
491   */ 488   */
492   template<capy::MutableBufferSequence MB> 489   template<capy::MutableBufferSequence MB>
HITCBC 493   6 [[nodiscard]] auto recv(MB const& buffers, corosio::message_flags flags) 490   6 [[nodiscard]] auto recv(MB const& buffers, corosio::message_flags flags)
494   { 491   {
HITCBC 495   6 native_recv_awaitable<MB> aw(*this, buffers, static_cast<int>(flags)); 492   6 native_recv_awaitable<MB> aw(*this, buffers, static_cast<int>(flags));
HITCBC 496   6 if (!is_open()) 493   6 if (!is_open())
HITCBC 497   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 494   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 498   6 return aw; 495   6 return aw;
499   } 496   }
500   497  
501   /// @overload 498   /// @overload
502   template<capy::MutableBufferSequence MB> 499   template<capy::MutableBufferSequence MB>
HITCBC 503   6 [[nodiscard]] auto recv(MB const& buffers) 500   6 [[nodiscard]] auto recv(MB const& buffers)
504   { 501   {
HITCBC 505   6 return recv(buffers, corosio::message_flags::none); 502   6 return recv(buffers, corosio::message_flags::none);
506   } 503   }
507   504  
508   /** Asynchronously wait for the socket to be ready. 505   /** Asynchronously wait for the socket to be ready.
509   506  
510   Calls the backend implementation directly, bypassing virtual 507   Calls the backend implementation directly, bypassing virtual
511   dispatch. Otherwise identical to @ref udp_socket::wait. 508   dispatch. Otherwise identical to @ref udp_socket::wait.
512   509  
513   @param w The wait direction (read, write, or error). 510   @param w The wait direction (read, write, or error).
514   511  
515   @return An awaitable yielding `io_result<>`. 512   @return An awaitable yielding `io_result<>`.
516   */ 513   */
HITCBC 517   4 [[nodiscard]] auto wait(wait_type w) 514   4 [[nodiscard]] auto wait(wait_type w)
518   { 515   {
HITCBC 519   4 return native_wait_awaitable(*this, w); 516   4 return native_wait_awaitable(*this, w);
520   } 517   }
521   }; 518   };
522   519  
523   } // namespace boost::corosio 520   } // namespace boost::corosio
524   521  
525   #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP 522   #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP