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