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