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