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