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