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