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