93.15% Lines (68/73) 100.00% Functions (24/24)
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_TCP_SOCKET_HPP 10   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
11   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP 11   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
12   12  
13   #include <boost/corosio/tcp_socket.hpp> 13   #include <boost/corosio/tcp_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_IOCP 29   #if BOOST_COROSIO_HAS_IOCP
30   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp> 30   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
31   #endif 31   #endif
  32 +
  33 + #if BOOST_COROSIO_HAS_IO_URING
  34 + #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp>
  35 + #endif
32   #endif // !BOOST_COROSIO_MRDOCS 36   #endif // !BOOST_COROSIO_MRDOCS
33   37  
34   namespace boost::corosio { 38   namespace boost::corosio {
35   39  
36   /** An asynchronous TCP socket with devirtualized I/O operations. 40   /** An asynchronous TCP socket with devirtualized I/O operations.
37   41  
38   This class template inherits from @ref tcp_socket and shadows 42   This class template inherits from @ref tcp_socket and shadows
39   the async operations (`read_some`, `write_some`, `connect`) with 43   the async operations (`read_some`, `write_some`, `connect`) with
40   versions that call the backend implementation directly, allowing 44   versions that call the backend implementation directly, allowing
41   the compiler to inline through the entire call chain. 45   the compiler to inline through the entire call chain.
42   46  
43   Non-async operations (`open`, `close`, `cancel`, socket options) 47   Non-async operations (`open`, `close`, `cancel`, socket options)
44   remain unchanged and dispatch through the compiled library. 48   remain unchanged and dispatch through the compiled library.
45   49  
46   A `native_tcp_socket` IS-A `tcp_socket` and can be passed to 50   A `native_tcp_socket` IS-A `tcp_socket` and can be passed to
47   any function expecting `tcp_socket&` or `io_stream&`, in which 51   any function expecting `tcp_socket&` or `io_stream&`, in which
48   case virtual dispatch is used transparently. 52   case virtual dispatch is used transparently.
49   53  
50   @tparam Backend A backend tag value (e.g., `epoll`, 54   @tparam Backend A backend tag value (e.g., `epoll`,
51   `iocp`) whose type provides the concrete implementation 55   `iocp`) whose type provides the concrete implementation
52   types. 56   types.
53   57  
54   @par Thread Safety 58   @par Thread Safety
55   Same as @ref tcp_socket. 59   Same as @ref tcp_socket.
56   60  
57   @par Example 61   @par Example
58   @code 62   @code
59   #include <boost/corosio/native/native_tcp_socket.hpp> 63   #include <boost/corosio/native/native_tcp_socket.hpp>
60   64  
61   native_io_context<epoll> ctx; 65   native_io_context<epoll> ctx;
62   native_tcp_socket<epoll> s(ctx); 66   native_tcp_socket<epoll> s(ctx);
63   s.open(); 67   s.open();
64   auto [ec] = co_await s.connect(ep); 68   auto [ec] = co_await s.connect(ep);
65   auto [ec2, n] = co_await s.read_some(buf); 69   auto [ec2, n] = co_await s.read_some(buf);
66   @endcode 70   @endcode
67   71  
68   @see tcp_socket, epoll_t, iocp_t 72   @see tcp_socket, epoll_t, iocp_t
69   */ 73   */
70   template<auto Backend> 74   template<auto Backend>
71   class native_tcp_socket : public tcp_socket 75   class native_tcp_socket : public tcp_socket
72   { 76   {
73   using backend_type = decltype(Backend); 77   using backend_type = decltype(Backend);
74   using impl_type = typename backend_type::tcp_socket_type; 78   using impl_type = typename backend_type::tcp_socket_type;
75   using service_type = typename backend_type::tcp_service_type; 79   using service_type = typename backend_type::tcp_service_type;
76   80  
HITCBC 77   22 impl_type& get_impl() noexcept 81   22 impl_type& get_impl() noexcept
78   { 82   {
HITCBC 79   22 return *static_cast<impl_type*>(h_.get()); 83   22 return *static_cast<impl_type*>(h_.get());
80   } 84   }
81   85  
82   template<class MutableBufferSequence> 86   template<class MutableBufferSequence>
83   struct native_read_awaitable 87   struct native_read_awaitable
84   { 88   {
85   native_tcp_socket& self_; 89   native_tcp_socket& self_;
86   MutableBufferSequence buffers_; 90   MutableBufferSequence buffers_;
87   std::stop_token token_; 91   std::stop_token token_;
88   mutable std::error_code ec_; 92   mutable std::error_code ec_;
89   mutable std::size_t bytes_transferred_ = 0; 93   mutable std::size_t bytes_transferred_ = 0;
90   94  
HITCBC 91   4 native_read_awaitable( 95   4 native_read_awaitable(
92   native_tcp_socket& self, MutableBufferSequence buffers) noexcept 96   native_tcp_socket& self, MutableBufferSequence buffers) noexcept
HITCBC 93   4 : self_(self) 97   4 : self_(self)
HITCBC 94   4 , buffers_(std::move(buffers)) 98   4 , buffers_(std::move(buffers))
95   { 99   {
HITCBC 96   4 } 100   4 }
97   101  
HITCBC 98   4 bool await_ready() const noexcept 102   4 bool await_ready() const noexcept
99   { 103   {
HITCBC 100   4 return token_.stop_requested(); 104   4 return token_.stop_requested();
101   } 105   }
102   106  
HITCBC 103   4 capy::io_result<std::size_t> await_resume() const noexcept 107   4 capy::io_result<std::size_t> await_resume() const noexcept
104   { 108   {
HITCBC 105   4 if (token_.stop_requested()) 109   4 if (token_.stop_requested())
MISUBC 106   return {make_error_code(std::errc::operation_canceled), 0}; 110   return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 107   4 return {ec_, bytes_transferred_}; 111   4 return {ec_, bytes_transferred_};
108   } 112   }
109   113  
HITCBC 110   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 114   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
111   -> std::coroutine_handle<> 115   -> std::coroutine_handle<>
112   { 116   {
HITCBC 113   4 token_ = env->stop_token; 117   4 token_ = env->stop_token;
HITCBC 114   12 return self_.get_impl().read_some( 118   12 return self_.get_impl().read_some(
HITCBC 115   12 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 119   12 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
116   } 120   }
117   }; 121   };
118   122  
119   template<class ConstBufferSequence> 123   template<class ConstBufferSequence>
120   struct native_write_awaitable 124   struct native_write_awaitable
121   { 125   {
122   native_tcp_socket& self_; 126   native_tcp_socket& self_;
123   ConstBufferSequence buffers_; 127   ConstBufferSequence buffers_;
124   std::stop_token token_; 128   std::stop_token token_;
125   mutable std::error_code ec_; 129   mutable std::error_code ec_;
126   mutable std::size_t bytes_transferred_ = 0; 130   mutable std::size_t bytes_transferred_ = 0;
127   131  
HITCBC 128   6 native_write_awaitable( 132   6 native_write_awaitable(
129   native_tcp_socket& self, ConstBufferSequence buffers) noexcept 133   native_tcp_socket& self, ConstBufferSequence buffers) noexcept
HITCBC 130   6 : self_(self) 134   6 : self_(self)
HITCBC 131   6 , buffers_(std::move(buffers)) 135   6 , buffers_(std::move(buffers))
132   { 136   {
HITCBC 133   6 } 137   6 }
134   138  
HITCBC 135   6 bool await_ready() const noexcept 139   6 bool await_ready() const noexcept
136   { 140   {
HITCBC 137   6 return token_.stop_requested(); 141   6 return token_.stop_requested();
138   } 142   }
139   143  
HITCBC 140   6 capy::io_result<std::size_t> await_resume() const noexcept 144   6 capy::io_result<std::size_t> await_resume() const noexcept
141   { 145   {
HITCBC 142   6 if (token_.stop_requested()) 146   6 if (token_.stop_requested())
MISUBC 143   return {make_error_code(std::errc::operation_canceled), 0}; 147   return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 144   6 return {ec_, bytes_transferred_}; 148   6 return {ec_, bytes_transferred_};
145   } 149   }
146   150  
HITCBC 147   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 151   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
148   -> std::coroutine_handle<> 152   -> std::coroutine_handle<>
149   { 153   {
HITCBC 150   6 token_ = env->stop_token; 154   6 token_ = env->stop_token;
HITCBC 151   18 return self_.get_impl().write_some( 155   18 return self_.get_impl().write_some(
HITCBC 152   18 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 156   18 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
153   } 157   }
154   }; 158   };
155   159  
156   struct native_wait_awaitable 160   struct native_wait_awaitable
157   { 161   {
158   native_tcp_socket& self_; 162   native_tcp_socket& self_;
159   wait_type w_; 163   wait_type w_;
160   std::stop_token token_; 164   std::stop_token token_;
161   mutable std::error_code ec_; 165   mutable std::error_code ec_;
162   166  
HITCBC 163   2 native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept 167   2 native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept
HITCBC 164   2 : self_(self) 168   2 : self_(self)
HITCBC 165   2 , w_(w) 169   2 , w_(w)
166   { 170   {
HITCBC 167   2 } 171   2 }
168   172  
HITCBC 169   2 bool await_ready() const noexcept 173   2 bool await_ready() const noexcept
170   { 174   {
HITCBC 171   2 return token_.stop_requested(); 175   2 return token_.stop_requested();
172   } 176   }
173   177  
HITCBC 174   2 capy::io_result<> await_resume() const noexcept 178   2 capy::io_result<> await_resume() const noexcept
175   { 179   {
HITCBC 176   2 if (token_.stop_requested()) 180   2 if (token_.stop_requested())
MISUBC 177   return {make_error_code(std::errc::operation_canceled)}; 181   return {make_error_code(std::errc::operation_canceled)};
HITCBC 178   2 return {ec_}; 182   2 return {ec_};
179   } 183   }
180   184  
HITCBC 181   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 185   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
182   -> std::coroutine_handle<> 186   -> std::coroutine_handle<>
183   { 187   {
HITCBC 184   2 token_ = env->stop_token; 188   2 token_ = env->stop_token;
HITCBC 185   6 return self_.get_impl().wait( 189   6 return self_.get_impl().wait(
HITCBC 186   6 h, env->executor, w_, token_, &ec_); 190   6 h, env->executor, w_, token_, &ec_);
187   } 191   }
188   }; 192   };
189   193  
190   struct native_connect_awaitable 194   struct native_connect_awaitable
191   { 195   {
192   native_tcp_socket& self_; 196   native_tcp_socket& self_;
193   endpoint endpoint_; 197   endpoint endpoint_;
194   std::stop_token token_; 198   std::stop_token token_;
195   mutable std::error_code ec_; 199   mutable std::error_code ec_;
196   200  
HITCBC 197   10 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept 201   10 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
HITCBC 198   10 : self_(self) 202   10 : self_(self)
HITCBC 199   10 , endpoint_(ep) 203   10 , endpoint_(ep)
200   { 204   {
HITCBC 201   10 } 205   10 }
202   206  
HITCBC 203   10 bool await_ready() const noexcept 207   10 bool await_ready() const noexcept
204   { 208   {
HITCBC 205   10 return token_.stop_requested(); 209   10 return token_.stop_requested();
206   } 210   }
207   211  
HITCBC 208   10 capy::io_result<> await_resume() const noexcept 212   10 capy::io_result<> await_resume() const noexcept
209   { 213   {
HITCBC 210   10 if (token_.stop_requested()) 214   10 if (token_.stop_requested())
MISUBC 211   return {make_error_code(std::errc::operation_canceled)}; 215   return {make_error_code(std::errc::operation_canceled)};
HITCBC 212   10 return {ec_}; 216   10 return {ec_};
213   } 217   }
214   218  
HITCBC 215   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 219   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
216   -> std::coroutine_handle<> 220   -> std::coroutine_handle<>
217   { 221   {
HITCBC 218   10 token_ = env->stop_token; 222   10 token_ = env->stop_token;
HITCBC 219   30 return self_.get_impl().connect( 223   30 return self_.get_impl().connect(
HITCBC 220   30 h, env->executor, endpoint_, token_, &ec_); 224   30 h, env->executor, endpoint_, token_, &ec_);
221   } 225   }
222   }; 226   };
223   227  
224   public: 228   public:
225   /** Construct a native socket from an execution context. 229   /** Construct a native socket from an execution context.
226   230  
227   @param ctx The execution context that will own this socket. 231   @param ctx The execution context that will own this socket.
228   */ 232   */
HITCBC 229   26 explicit native_tcp_socket(capy::execution_context& ctx) 233   26 explicit native_tcp_socket(capy::execution_context& ctx)
HITCBC 230   26 : io_object(create_handle<service_type>(ctx)) 234   26 : io_object(create_handle<service_type>(ctx))
231   { 235   {
HITCBC 232   26 } 236   26 }
233   237  
234   /** Construct a native socket from an executor. 238   /** Construct a native socket from an executor.
235   239  
236   @param ex The executor whose context will own the socket. 240   @param ex The executor whose context will own the socket.
237   */ 241   */
238   template<class Ex> 242   template<class Ex>
239   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) && 243   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
240   capy::Executor<Ex> 244   capy::Executor<Ex>
241   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context()) 245   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
242   { 246   {
243   } 247   }
244   248  
245   /** Move construct. 249   /** Move construct.
246   250  
247   @param other The socket to move from. 251   @param other The socket to move from.
248   252  
249   @pre No awaitables returned by @p other's methods exist. 253   @pre No awaitables returned by @p other's methods exist.
250   @pre @p other is not referenced as a peer in any outstanding 254   @pre @p other is not referenced as a peer in any outstanding
251   accept awaitable. 255   accept awaitable.
252   @pre The execution context associated with @p other must 256   @pre The execution context associated with @p other must
253   outlive this socket. 257   outlive this socket.
254   */ 258   */
HITCBC 255   14 native_tcp_socket(native_tcp_socket&&) noexcept = default; 259   14 native_tcp_socket(native_tcp_socket&&) noexcept = default;
256   260  
257   /** Move assign. 261   /** Move assign.
258   262  
259   @param other The socket to move from. 263   @param other The socket to move from.
260   264  
261   @pre No awaitables returned by either `*this` or @p other's 265   @pre No awaitables returned by either `*this` or @p other's
262   methods exist. 266   methods exist.
263   @pre Neither `*this` nor @p other is referenced as a peer in 267   @pre Neither `*this` nor @p other is referenced as a peer in
264   any outstanding accept awaitable. 268   any outstanding accept awaitable.
265   @pre The execution context associated with @p other must 269   @pre The execution context associated with @p other must
266   outlive this socket. 270   outlive this socket.
267   */ 271   */
HITCBC 268   2 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default; 272   2 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
269   273  
270   native_tcp_socket(native_tcp_socket const&) = delete; 274   native_tcp_socket(native_tcp_socket const&) = delete;
271   native_tcp_socket& operator=(native_tcp_socket const&) = delete; 275   native_tcp_socket& operator=(native_tcp_socket const&) = delete;
272   276  
273   /** Asynchronously read data from the socket. 277   /** Asynchronously read data from the socket.
274   278  
275   Calls the backend implementation directly, bypassing virtual 279   Calls the backend implementation directly, bypassing virtual
276   dispatch. Otherwise identical to @ref io_stream::read_some. 280   dispatch. Otherwise identical to @ref io_stream::read_some.
277   281  
278   @param buffers The buffer sequence to read into. 282   @param buffers The buffer sequence to read into.
279   283  
280   @return An awaitable yielding `(error_code, std::size_t)`. 284   @return An awaitable yielding `(error_code, std::size_t)`.
281   285  
282   This socket must outlive the returned awaitable. The memory 286   This socket must outlive the returned awaitable. The memory
283   referenced by @p buffers must remain valid until the operation 287   referenced by @p buffers must remain valid until the operation
284   completes. 288   completes.
285   */ 289   */
286   template<capy::MutableBufferSequence MB> 290   template<capy::MutableBufferSequence MB>
HITCBC 287   4 auto read_some(MB const& buffers) 291   4 auto read_some(MB const& buffers)
288   { 292   {
HITCBC 289   4 return native_read_awaitable<MB>(*this, buffers); 293   4 return native_read_awaitable<MB>(*this, buffers);
290   } 294   }
291   295  
292   /** Asynchronously write data to the socket. 296   /** Asynchronously write data to the socket.
293   297  
294   Calls the backend implementation directly, bypassing virtual 298   Calls the backend implementation directly, bypassing virtual
295   dispatch. Otherwise identical to @ref io_stream::write_some. 299   dispatch. Otherwise identical to @ref io_stream::write_some.
296   300  
297   @param buffers The buffer sequence to write from. 301   @param buffers The buffer sequence to write from.
298   302  
299   @return An awaitable yielding `(error_code, std::size_t)`. 303   @return An awaitable yielding `(error_code, std::size_t)`.
300   304  
301   This socket must outlive the returned awaitable. The memory 305   This socket must outlive the returned awaitable. The memory
302   referenced by @p buffers must remain valid until the operation 306   referenced by @p buffers must remain valid until the operation
303   completes. 307   completes.
304   */ 308   */
305   template<capy::ConstBufferSequence CB> 309   template<capy::ConstBufferSequence CB>
HITCBC 306   6 auto write_some(CB const& buffers) 310   6 auto write_some(CB const& buffers)
307   { 311   {
HITCBC 308   6 return native_write_awaitable<CB>(*this, buffers); 312   6 return native_write_awaitable<CB>(*this, buffers);
309   } 313   }
310   314  
311   /** Asynchronously connect to a remote endpoint. 315   /** Asynchronously connect to a remote endpoint.
312   316  
313   Calls the backend implementation directly, bypassing virtual 317   Calls the backend implementation directly, bypassing virtual
314   dispatch. Otherwise identical to @ref tcp_socket::connect. 318   dispatch. Otherwise identical to @ref tcp_socket::connect.
315   319  
316   @param ep The remote endpoint to connect to. 320   @param ep The remote endpoint to connect to.
317   321  
318   @return An awaitable yielding `io_result<>`. 322   @return An awaitable yielding `io_result<>`.
319   323  
320   @throws std::logic_error if the socket is not open. 324   @throws std::logic_error if the socket is not open.
321   325  
322   This socket must outlive the returned awaitable. 326   This socket must outlive the returned awaitable.
323   */ 327   */
HITCBC 324   10 auto connect(endpoint ep) 328   10 auto connect(endpoint ep)
325   { 329   {
HITCBC 326   10 if (!is_open()) 330   10 if (!is_open())
MISUBC 327   detail::throw_logic_error("connect: socket not open"); 331   detail::throw_logic_error("connect: socket not open");
HITCBC 328   10 return native_connect_awaitable(*this, ep); 332   10 return native_connect_awaitable(*this, ep);
329   } 333   }
330   334  
331   /** Asynchronously wait for the socket to be ready. 335   /** Asynchronously wait for the socket to be ready.
332   336  
333   Calls the backend implementation directly, bypassing virtual 337   Calls the backend implementation directly, bypassing virtual
334   dispatch. Otherwise identical to @ref tcp_socket::wait. 338   dispatch. Otherwise identical to @ref tcp_socket::wait.
335   339  
336   @param w The wait direction (read, write, or error). 340   @param w The wait direction (read, write, or error).
337   341  
338   @return An awaitable yielding `io_result<>`. 342   @return An awaitable yielding `io_result<>`.
339   */ 343   */
HITCBC 340   2 [[nodiscard]] auto wait(wait_type w) 344   2 [[nodiscard]] auto wait(wait_type w)
341   { 345   {
HITCBC 342   2 return native_wait_awaitable(*this, w); 346   2 return native_wait_awaitable(*this, w);
343   } 347   }
344   }; 348   };
345   349  
346   } // namespace boost::corosio 350   } // namespace boost::corosio
347   351  
348   #endif 352   #endif