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