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   #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   22 impl_type& get_impl() noexcept 77   22 impl_type& get_impl() noexcept
78   { 78   {
HITCBC 79   22 return *static_cast<impl_type*>(h_.get()); 79   22 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   6 native_write_awaitable( 128   6 native_write_awaitable(
129   native_tcp_socket& self, ConstBufferSequence buffers) noexcept 129   native_tcp_socket& self, ConstBufferSequence buffers) noexcept
HITCBC 130   6 : self_(self) 130   6 : self_(self)
HITCBC 131   6 , buffers_(std::move(buffers)) 131   6 , buffers_(std::move(buffers))
132   { 132   {
HITCBC 133   6 } 133   6 }
134   134  
HITCBC 135   6 bool await_ready() const noexcept 135   6 bool await_ready() const noexcept
136   { 136   {
HITCBC 137   6 return token_.stop_requested(); 137   6 return token_.stop_requested();
138   } 138   }
139   139  
HITCBC 140   6 capy::io_result<std::size_t> await_resume() const noexcept 140   6 capy::io_result<std::size_t> await_resume() const noexcept
141   { 141   {
HITCBC 142   6 if (token_.stop_requested()) 142   6 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   6 return {ec_, bytes_transferred_}; 144   6 return {ec_, bytes_transferred_};
145   } 145   }
146   146  
HITCBC 147   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 147   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
148   -> std::coroutine_handle<> 148   -> std::coroutine_handle<>
149   { 149   {
HITCBC 150   6 token_ = env->stop_token; 150   6 token_ = env->stop_token;
HITCBC 151   18 return self_.get_impl().write_some( 151   18 return self_.get_impl().write_some(
HITCBC 152   18 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 152   18 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
153   } 153   }
154   }; 154   };
155   155  
156   struct native_wait_awaitable 156   struct native_wait_awaitable
157   { 157   {
158   native_tcp_socket& self_; 158   native_tcp_socket& self_;
159   wait_type w_; 159   wait_type w_;
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   2 native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept 163   2 native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept
HITCBC 164   2 : self_(self) 164   2 : self_(self)
HITCBC 165   2 , w_(w) 165   2 , w_(w)
166   { 166   {
HITCBC 167   2 } 167   2 }
168   168  
HITCBC 169   2 bool await_ready() const noexcept 169   2 bool await_ready() const noexcept
170   { 170   {
HITCBC 171   2 return token_.stop_requested(); 171   2 return token_.stop_requested();
172   } 172   }
173   173  
HITCBC 174   2 capy::io_result<> await_resume() const noexcept 174   2 capy::io_result<> await_resume() const noexcept
175   { 175   {
HITCBC 176   2 if (token_.stop_requested()) 176   2 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   2 return {ec_}; 178   2 return {ec_};
179   } 179   }
180   180  
HITCBC 181   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 181   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
182   -> std::coroutine_handle<> 182   -> std::coroutine_handle<>
183   { 183   {
HITCBC 184   2 token_ = env->stop_token; 184   2 token_ = env->stop_token;
HITCBC 185   6 return self_.get_impl().wait( 185   6 return self_.get_impl().wait(
HITCBC 186   6 h, env->executor, w_, token_, &ec_); 186   6 h, env->executor, w_, token_, &ec_);
187   } 187   }
188   }; 188   };
189   189  
190   struct native_connect_awaitable 190   struct native_connect_awaitable
191   { 191   {
192   native_tcp_socket& self_; 192   native_tcp_socket& self_;
193   endpoint endpoint_; 193   endpoint endpoint_;
194   std::stop_token token_; 194   std::stop_token token_;
195   mutable std::error_code ec_; 195   mutable std::error_code ec_;
196   196  
HITCBC 197   10 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept 197   10 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
HITCBC 198   10 : self_(self) 198   10 : self_(self)
HITCBC 199   10 , endpoint_(ep) 199   10 , endpoint_(ep)
200   { 200   {
HITCBC 201   10 } 201   10 }
202   202  
HITCBC 203   10 bool await_ready() const noexcept 203   10 bool await_ready() const noexcept
204   { 204   {
HITCBC 205   10 return token_.stop_requested(); 205   10 return token_.stop_requested();
206   } 206   }
207   207  
HITCBC 208   10 capy::io_result<> await_resume() const noexcept 208   10 capy::io_result<> await_resume() const noexcept
209   { 209   {
HITCBC 210   10 if (token_.stop_requested()) 210   10 if (token_.stop_requested())
MISUBC 211   return {make_error_code(std::errc::operation_canceled)}; 211   return {make_error_code(std::errc::operation_canceled)};
HITCBC 212   10 return {ec_}; 212   10 return {ec_};
213   } 213   }
214   214  
HITCBC 215   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 215   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
216   -> std::coroutine_handle<> 216   -> std::coroutine_handle<>
217   { 217   {
HITCBC 218   10 token_ = env->stop_token; 218   10 token_ = env->stop_token;
HITCBC 219   30 return self_.get_impl().connect( 219   30 return self_.get_impl().connect(
HITCBC 220   30 h, env->executor, endpoint_, token_, &ec_); 220   30 h, env->executor, endpoint_, token_, &ec_);
221   } 221   }
222   }; 222   };
223   223  
224   public: 224   public:
225   /** Construct a native socket from an execution context. 225   /** Construct a native socket from an execution context.
226   226  
227   @param ctx The execution context that will own this socket. 227   @param ctx The execution context that will own this socket.
228   */ 228   */
HITCBC 229   26 explicit native_tcp_socket(capy::execution_context& ctx) 229   26 explicit native_tcp_socket(capy::execution_context& ctx)
HITCBC 230   26 : io_object(create_handle<service_type>(ctx)) 230   26 : io_object(create_handle<service_type>(ctx))
231   { 231   {
HITCBC 232   26 } 232   26 }
233   233  
234   /** Construct a native socket from an executor. 234   /** Construct a native socket from an executor.
235   235  
236   @param ex The executor whose context will own the socket. 236   @param ex The executor whose context will own the socket.
237   */ 237   */
238   template<class Ex> 238   template<class Ex>
239   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) && 239   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
240   capy::Executor<Ex> 240   capy::Executor<Ex>
241   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context()) 241   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
242   { 242   {
243   } 243   }
244   244  
245   /** Move construct. 245   /** Move construct.
246   246  
247   @param other The socket to move from. 247   @param other The socket to move from.
248   248  
249   @pre No awaitables returned by @p other's methods exist. 249   @pre No awaitables returned by @p other's methods exist.
250   @pre @p other is not referenced as a peer in any outstanding 250   @pre @p other is not referenced as a peer in any outstanding
251   accept awaitable. 251   accept awaitable.
252   @pre The execution context associated with @p other must 252   @pre The execution context associated with @p other must
253   outlive this socket. 253   outlive this socket.
254   */ 254   */
HITCBC 255   14 native_tcp_socket(native_tcp_socket&&) noexcept = default; 255   14 native_tcp_socket(native_tcp_socket&&) noexcept = default;
256   256  
257   /** Move assign. 257   /** Move assign.
258   258  
259   @param other The socket to move from. 259   @param other The socket to move from.
260   260  
261   @pre No awaitables returned by either `*this` or @p other's 261   @pre No awaitables returned by either `*this` or @p other's
262   methods exist. 262   methods exist.
263   @pre Neither `*this` nor @p other is referenced as a peer in 263   @pre Neither `*this` nor @p other is referenced as a peer in
264   any outstanding accept awaitable. 264   any outstanding accept awaitable.
265   @pre The execution context associated with @p other must 265   @pre The execution context associated with @p other must
266   outlive this socket. 266   outlive this socket.
267   */ 267   */
HITCBC 268   2 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default; 268   2 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
269   269  
270   native_tcp_socket(native_tcp_socket const&) = delete; 270   native_tcp_socket(native_tcp_socket const&) = delete;
271   native_tcp_socket& operator=(native_tcp_socket const&) = delete; 271   native_tcp_socket& operator=(native_tcp_socket const&) = delete;
272   272  
273   /** Asynchronously read data from the socket. 273   /** Asynchronously read data from the socket.
274   274  
275   Calls the backend implementation directly, bypassing virtual 275   Calls the backend implementation directly, bypassing virtual
276   dispatch. Otherwise identical to @ref io_stream::read_some. 276   dispatch. Otherwise identical to @ref io_stream::read_some.
277   277  
278   @param buffers The buffer sequence to read into. 278   @param buffers The buffer sequence to read into.
279   279  
280   @return An awaitable yielding `(error_code, std::size_t)`. 280   @return An awaitable yielding `(error_code, std::size_t)`.
281   281  
282   This socket must outlive the returned awaitable. The memory 282   This socket must outlive the returned awaitable. The memory
283   referenced by @p buffers must remain valid until the operation 283   referenced by @p buffers must remain valid until the operation
284   completes. 284   completes.
285   */ 285   */
286   template<capy::MutableBufferSequence MB> 286   template<capy::MutableBufferSequence MB>
HITCBC 287   4 auto read_some(MB const& buffers) 287   4 auto read_some(MB const& buffers)
288   { 288   {
HITCBC 289   4 return native_read_awaitable<MB>(*this, buffers); 289   4 return native_read_awaitable<MB>(*this, buffers);
290   } 290   }
291   291  
292   /** Asynchronously write data to the socket. 292   /** Asynchronously write data to the socket.
293   293  
294   Calls the backend implementation directly, bypassing virtual 294   Calls the backend implementation directly, bypassing virtual
295   dispatch. Otherwise identical to @ref io_stream::write_some. 295   dispatch. Otherwise identical to @ref io_stream::write_some.
296   296  
297   @param buffers The buffer sequence to write from. 297   @param buffers The buffer sequence to write from.
298   298  
299   @return An awaitable yielding `(error_code, std::size_t)`. 299   @return An awaitable yielding `(error_code, std::size_t)`.
300   300  
301   This socket must outlive the returned awaitable. The memory 301   This socket must outlive the returned awaitable. The memory
302   referenced by @p buffers must remain valid until the operation 302   referenced by @p buffers must remain valid until the operation
303   completes. 303   completes.
304   */ 304   */
305   template<capy::ConstBufferSequence CB> 305   template<capy::ConstBufferSequence CB>
HITCBC 306   6 auto write_some(CB const& buffers) 306   6 auto write_some(CB const& buffers)
307   { 307   {
HITCBC 308   6 return native_write_awaitable<CB>(*this, buffers); 308   6 return native_write_awaitable<CB>(*this, buffers);
309   } 309   }
310   310  
311   /** Asynchronously connect to a remote endpoint. 311   /** Asynchronously connect to a remote endpoint.
312   312  
313   Calls the backend implementation directly, bypassing virtual 313   Calls the backend implementation directly, bypassing virtual
314   dispatch. Otherwise identical to @ref tcp_socket::connect. 314   dispatch. Otherwise identical to @ref tcp_socket::connect.
315   315  
316   @param ep The remote endpoint to connect to. 316   @param ep The remote endpoint to connect to.
317   317  
318   @return An awaitable yielding `io_result<>`. 318   @return An awaitable yielding `io_result<>`.
319   319  
320   @throws std::logic_error if the socket is not open. 320   @throws std::logic_error if the socket is not open.
321   321  
322   This socket must outlive the returned awaitable. 322   This socket must outlive the returned awaitable.
323   */ 323   */
HITCBC 324   10 auto connect(endpoint ep) 324   10 auto connect(endpoint ep)
325   { 325   {
HITCBC 326   10 if (!is_open()) 326   10 if (!is_open())
MISUBC 327   detail::throw_logic_error("connect: socket not open"); 327   detail::throw_logic_error("connect: socket not open");
HITCBC 328   10 return native_connect_awaitable(*this, ep); 328   10 return native_connect_awaitable(*this, ep);
329   } 329   }
330   330  
331   /** Asynchronously wait for the socket to be ready. 331   /** Asynchronously wait for the socket to be ready.
332   332  
333   Calls the backend implementation directly, bypassing virtual 333   Calls the backend implementation directly, bypassing virtual
334   dispatch. Otherwise identical to @ref tcp_socket::wait. 334   dispatch. Otherwise identical to @ref tcp_socket::wait.
335   335  
336   @param w The wait direction (read, write, or error). 336   @param w The wait direction (read, write, or error).
337   337  
338   @return An awaitable yielding `io_result<>`. 338   @return An awaitable yielding `io_result<>`.
339   */ 339   */
HITCBC 340   2 [[nodiscard]] auto wait(wait_type w) 340   2 [[nodiscard]] auto wait(wait_type w)
341   { 341   {
HITCBC 342   2 return native_wait_awaitable(*this, w); 342   2 return native_wait_awaitable(*this, w);
343   } 343   }
344   }; 344   };
345   345  
346   } // namespace boost::corosio 346   } // namespace boost::corosio
347   347  
348   #endif 348   #endif