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   #include <boost/corosio/detail/op_base.hpp>
17   17  
18   #ifndef BOOST_COROSIO_MRDOCS 18   #ifndef BOOST_COROSIO_MRDOCS
19   #if BOOST_COROSIO_HAS_EPOLL 19   #if BOOST_COROSIO_HAS_EPOLL
20   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 20   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
21   #endif 21   #endif
22   22  
23   #if BOOST_COROSIO_HAS_SELECT 23   #if BOOST_COROSIO_HAS_SELECT
24   #include <boost/corosio/native/detail/select/select_types.hpp> 24   #include <boost/corosio/native/detail/select/select_types.hpp>
25   #endif 25   #endif
26   26  
27   #if BOOST_COROSIO_HAS_KQUEUE 27   #if BOOST_COROSIO_HAS_KQUEUE
28   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 28   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
29   #endif 29   #endif
30   30  
31   #if BOOST_COROSIO_HAS_IOCP 31   #if BOOST_COROSIO_HAS_IOCP
32   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp> 32   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
33   #endif 33   #endif
34   34  
35   #if BOOST_COROSIO_HAS_URING 35   #if BOOST_COROSIO_HAS_URING
36   #include <boost/corosio/native/detail/uring/uring_types.hpp> 36   #include <boost/corosio/native/detail/uring/uring_types.hpp>
37   #endif 37   #endif
38   #endif // !BOOST_COROSIO_MRDOCS 38   #endif // !BOOST_COROSIO_MRDOCS
39   39  
40   namespace boost::corosio { 40   namespace boost::corosio {
41   41  
42 - /** Connects, reads, and writes over TCP, calling the backend directly. 42 + /** An asynchronous TCP socket with devirtualized I/O operations.
43   43  
44 - This class template inherits from @ref tcp_socket. It shadows the 44 + This class template inherits from @ref tcp_socket and shadows
45 - async operations (`read_some`, `write_some`, `connect`) with 45 + the async operations (`read_some`, `write_some`, `connect`) with
46 - versions that call the backend implementation directly. The compiler 46 + versions that call the backend implementation directly, allowing
47 - can then inline through the entire call chain. 47 + the compiler to inline through the entire call chain.
48   48  
49   Non-async operations (`open`, `close`, `cancel`, socket options) 49   Non-async operations (`open`, `close`, `cancel`, socket options)
50   remain unchanged and dispatch through the compiled library. 50   remain unchanged and dispatch through the compiled library.
51   51  
52   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
53 - any function expecting `tcp_socket&` or `io_stream&`. In that 53 + any function expecting `tcp_socket&` or `io_stream&`, in which
54 - case, virtual dispatch is used transparently. 54 + case virtual dispatch is used transparently.
55   55  
56   @tparam Backend A backend tag value (e.g., `epoll`, 56   @tparam Backend A backend tag value (e.g., `epoll`,
57   `iocp`) whose type provides the concrete implementation 57   `iocp`) whose type provides the concrete implementation
58   types. 58   types.
59   59  
60   @par Thread Safety 60   @par Thread Safety
61   Same as @ref tcp_socket. 61   Same as @ref tcp_socket.
62   62  
63   @par Example 63   @par Example
64   @par !example native_tcp_socket 64   @par !example native_tcp_socket
65   65  
66   @see tcp_socket, epoll_t, iocp_t 66   @see tcp_socket, epoll_t, iocp_t
67   */ 67   */
68   template<auto Backend> 68   template<auto Backend>
69   class native_tcp_socket : public tcp_socket 69   class native_tcp_socket : public tcp_socket
70   { 70   {
71   using backend_type = decltype(Backend); 71   using backend_type = decltype(Backend);
72   using impl_type = typename backend_type::tcp_socket_type; 72   using impl_type = typename backend_type::tcp_socket_type;
73   using service_type = typename backend_type::tcp_service_type; 73   using service_type = typename backend_type::tcp_service_type;
74   74  
HITCBC 75   51 impl_type& get_impl() noexcept 75   51 impl_type& get_impl() noexcept
76   { 76   {
HITCBC 77   51 return *static_cast<impl_type*>(h_.get()); 77   51 return *static_cast<impl_type*>(h_.get());
78   } 78   }
79   79  
80   template<class MutableBufferSequence> 80   template<class MutableBufferSequence>
81   struct native_read_awaitable 81   struct native_read_awaitable
82   : detail::bytes_op_base<native_read_awaitable<MutableBufferSequence>> 82   : detail::bytes_op_base<native_read_awaitable<MutableBufferSequence>>
83   { 83   {
84   native_tcp_socket& self_; 84   native_tcp_socket& self_;
85   MutableBufferSequence buffers_; 85   MutableBufferSequence buffers_;
86   86  
HITCBC 87   14 native_read_awaitable( 87   14 native_read_awaitable(
88   native_tcp_socket& self, MutableBufferSequence buffers) noexcept 88   native_tcp_socket& self, MutableBufferSequence buffers) noexcept
HITCBC 89   14 : self_(self) 89   14 : self_(self)
HITCBC 90   14 , buffers_(std::move(buffers)) 90   14 , buffers_(std::move(buffers))
91   { 91   {
HITCBC 92   14 } 92   14 }
93   93  
94   std::coroutine_handle<> 94   std::coroutine_handle<>
HITCBC 95   10 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 95   10 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
96   { 96   {
HITCBC 97   30 return self_.get_impl().read_some( 97   30 return self_.get_impl().read_some(
HITCBC 98   30 h, ex, buffers_, this->token_, &this->ec_, &this->bytes_); 98   30 h, ex, buffers_, this->token_, &this->ec_, &this->bytes_);
99   } 99   }
100   }; 100   };
101   101  
102   template<class ConstBufferSequence> 102   template<class ConstBufferSequence>
103   struct native_write_awaitable 103   struct native_write_awaitable
104   : detail::bytes_op_base<native_write_awaitable<ConstBufferSequence>> 104   : detail::bytes_op_base<native_write_awaitable<ConstBufferSequence>>
105   { 105   {
106   native_tcp_socket& self_; 106   native_tcp_socket& self_;
107   ConstBufferSequence buffers_; 107   ConstBufferSequence buffers_;
108   108  
HITCBC 109   14 native_write_awaitable( 109   14 native_write_awaitable(
110   native_tcp_socket& self, ConstBufferSequence buffers) noexcept 110   native_tcp_socket& self, ConstBufferSequence buffers) noexcept
HITCBC 111   14 : self_(self) 111   14 : self_(self)
HITCBC 112   14 , buffers_(std::move(buffers)) 112   14 , buffers_(std::move(buffers))
113   { 113   {
HITCBC 114   14 } 114   14 }
115   115  
116   std::coroutine_handle<> 116   std::coroutine_handle<>
HITCBC 117   12 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 117   12 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
118   { 118   {
HITCBC 119   36 return self_.get_impl().write_some( 119   36 return self_.get_impl().write_some(
HITCBC 120   36 h, ex, buffers_, this->token_, &this->ec_, &this->bytes_); 120   36 h, ex, buffers_, this->token_, &this->ec_, &this->bytes_);
121   } 121   }
122   }; 122   };
123   123  
124   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable> 124   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable>
125   { 125   {
126   native_tcp_socket& self_; 126   native_tcp_socket& self_;
127   wait_type w_; 127   wait_type w_;
128   128  
HITCBC 129   10 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 130   10 : self_(self) 130   10 : self_(self)
HITCBC 131   10 , w_(w) 131   10 , w_(w)
132   { 132   {
HITCBC 133   10 } 133   10 }
134   134  
135   std::coroutine_handle<> 135   std::coroutine_handle<>
HITCBC 136   8 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 136   8 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
137   { 137   {
HITCBC 138   8 return self_.get_impl().wait(h, ex, w_, this->token_, &this->ec_); 138   8 return self_.get_impl().wait(h, ex, w_, this->token_, &this->ec_);
139   } 139   }
140   }; 140   };
141   141  
142   struct native_connect_awaitable 142   struct native_connect_awaitable
143   : detail::void_op_base<native_connect_awaitable> 143   : detail::void_op_base<native_connect_awaitable>
144   { 144   {
145   native_tcp_socket& self_; 145   native_tcp_socket& self_;
146   endpoint endpoint_; 146   endpoint endpoint_;
147   147  
HITCBC 148   23 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept 148   23 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
HITCBC 149   23 : self_(self) 149   23 : self_(self)
HITCBC 150   23 , endpoint_(ep) 150   23 , endpoint_(ep)
151   { 151   {
HITCBC 152   23 } 152   23 }
153   153  
154   std::coroutine_handle<> 154   std::coroutine_handle<>
HITCBC 155   21 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 155   21 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
156   { 156   {
HITCBC 157   63 return self_.get_impl().connect( 157   63 return self_.get_impl().connect(
HITCBC 158   63 h, ex, endpoint_, this->token_, &this->ec_); 158   63 h, ex, endpoint_, this->token_, &this->ec_);
159   } 159   }
160   }; 160   };
161   161  
162   public: 162   public:
163   /** Construct a native socket from an execution context. 163   /** Construct a native socket from an execution context.
164   164  
165 - @param ctx The execution context that owns this socket. 165 + @param ctx The execution context that will own this socket.
166   */ 166   */
HITCBC 167   49 explicit native_tcp_socket(capy::execution_context& ctx) 167   49 explicit native_tcp_socket(capy::execution_context& ctx)
HITCBC 168   49 : io_object(create_handle<service_type>(ctx)) 168   49 : io_object(create_handle<service_type>(ctx))
169   { 169   {
HITCBC 170   49 } 170   49 }
171   171  
172   /** Construct a native socket from an executor. 172   /** Construct a native socket from an executor.
173   173  
174 - @param ex The executor whose context owns the socket. 174 + @param ex The executor whose context will own the socket.
175 -  
176 - @tparam Ex A type satisfying @ref capy::Executor. Must not  
177 - be `native_tcp_socket` itself (disables implicit  
178 - conversion from move).  
179   */ 175   */
180   template<class Ex> 176   template<class Ex>
181   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>) &&
182   capy::Executor<Ex> 178   capy::Executor<Ex>
183   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())
184   { 180   {
185   } 181   }
186   182  
187   /** Move construct. 183   /** Move construct.
188   184  
189   @param other The socket to move from. 185   @param other The socket to move from.
190   186  
191   @pre No awaitables returned by @p other's methods exist. 187   @pre No awaitables returned by @p other's methods exist.
192   @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
193   accept awaitable. 189   accept awaitable.
194   @pre The execution context associated with @p other must 190   @pre The execution context associated with @p other must
195   outlive this socket. 191   outlive this socket.
196   */ 192   */
HITCBC 197   28 native_tcp_socket(native_tcp_socket&&) noexcept = default; 193   28 native_tcp_socket(native_tcp_socket&&) noexcept = default;
198   194  
199   /** Move assign. 195   /** Move assign.
200   196  
201   @param other The socket to move from. 197   @param other The socket to move from.
202   198  
203   @pre No awaitables returned by either `*this` or @p other's 199   @pre No awaitables returned by either `*this` or @p other's
204   methods exist. 200   methods exist.
205   @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
206   any outstanding accept awaitable. 202   any outstanding accept awaitable.
207   @pre The execution context associated with @p other must 203   @pre The execution context associated with @p other must
208   outlive this socket. 204   outlive this socket.
209   */ 205   */
HITCBC 210   3 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default; 206   3 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
211   207  
212 - /// Copy construction is disabled; the handle is uniquely owned. 208 + native_tcp_socket(native_tcp_socket const&) = delete;
213 - native_tcp_socket(native_tcp_socket const&) = delete;  
214 - /// Copy assignment is disabled; the handle is uniquely owned.  
215   native_tcp_socket& operator=(native_tcp_socket const&) = delete; 209   native_tcp_socket& operator=(native_tcp_socket const&) = delete;
216   210  
217   /** Asynchronously read data from the socket. 211   /** Asynchronously read data from the socket.
218   212  
219   Calls the backend implementation directly, bypassing virtual 213   Calls the backend implementation directly, bypassing virtual
220   dispatch. Otherwise identical to @ref io_stream::read_some. 214   dispatch. Otherwise identical to @ref io_stream::read_some.
221   215  
222   @param buffers The buffer sequence to read into. 216   @param buffers The buffer sequence to read into.
223   217  
224   @return An awaitable yielding `(error_code, std::size_t)`. 218   @return An awaitable yielding `(error_code, std::size_t)`.
225   219  
226   This socket must outlive the returned awaitable. The memory 220   This socket must outlive the returned awaitable. The memory
227   referenced by @p buffers must remain valid until the operation 221   referenced by @p buffers must remain valid until the operation
228   completes. 222   completes.
229   */ 223   */
230   template<capy::MutableBufferSequence MB> 224   template<capy::MutableBufferSequence MB>
HITCBC 231   14 [[nodiscard]] auto read_some(MB const& buffers) 225   14 [[nodiscard]] auto read_some(MB const& buffers)
232   { 226   {
HITCBC 233   14 return native_read_awaitable<MB>(*this, buffers); 227   14 return native_read_awaitable<MB>(*this, buffers);
234   } 228   }
235   229  
236   /** Asynchronously write data to the socket. 230   /** Asynchronously write data to the socket.
237   231  
238   Calls the backend implementation directly, bypassing virtual 232   Calls the backend implementation directly, bypassing virtual
239   dispatch. Otherwise identical to @ref io_stream::write_some. 233   dispatch. Otherwise identical to @ref io_stream::write_some.
240   234  
241   @param buffers The buffer sequence to write from. 235   @param buffers The buffer sequence to write from.
242   236  
243   @return An awaitable yielding `(error_code, std::size_t)`. 237   @return An awaitable yielding `(error_code, std::size_t)`.
244   238  
245   This socket must outlive the returned awaitable. The memory 239   This socket must outlive the returned awaitable. The memory
246   referenced by @p buffers must remain valid until the operation 240   referenced by @p buffers must remain valid until the operation
247   completes. 241   completes.
248   */ 242   */
249   template<capy::ConstBufferSequence CB> 243   template<capy::ConstBufferSequence CB>
HITCBC 250   14 [[nodiscard]] auto write_some(CB const& buffers) 244   14 [[nodiscard]] auto write_some(CB const& buffers)
251   { 245   {
HITCBC 252   14 return native_write_awaitable<CB>(*this, buffers); 246   14 return native_write_awaitable<CB>(*this, buffers);
253   } 247   }
254   248  
255   /** Asynchronously connect to a remote endpoint. 249   /** Asynchronously connect to a remote endpoint.
256   250  
257   Calls the backend implementation directly, bypassing virtual 251   Calls the backend implementation directly, bypassing virtual
258   dispatch. Otherwise identical to @ref tcp_socket::connect. 252   dispatch. Otherwise identical to @ref tcp_socket::connect.
259   253  
260   If the socket is not open, it is opened automatically using 254   If the socket is not open, it is opened automatically using
261   the protocol matching the endpoint's address family. An open 255   the protocol matching the endpoint's address family. An open
262   failure surfaces through the connect completion. 256   failure surfaces through the connect completion.
263   257  
264   @param ep The remote endpoint to connect to. 258   @param ep The remote endpoint to connect to.
265   259  
266   @return An awaitable yielding `io_result<>`. 260   @return An awaitable yielding `io_result<>`.
267   261  
268   This socket must outlive the returned awaitable. 262   This socket must outlive the returned awaitable.
269   */ 263   */
HITCBC 270   23 [[nodiscard]] auto connect(endpoint ep) 264   23 [[nodiscard]] auto connect(endpoint ep)
271   { 265   {
HITCBC 272   23 native_connect_awaitable aw(*this, ep); 266   23 native_connect_awaitable aw(*this, ep);
HITCBC 273   23 if (!is_open()) 267   23 if (!is_open())
HITCBC 274   2 aw.ec_ = open(ep.address().family()); 268   2 aw.ec_ = open(ep.address().family());
HITCBC 275   23 return aw; 269   23 return aw;
276   } 270   }
277   271  
278   /** Asynchronously wait for the socket to be ready. 272   /** Asynchronously wait for the socket to be ready.
279   273  
280   Calls the backend implementation directly, bypassing virtual 274   Calls the backend implementation directly, bypassing virtual
281   dispatch. Otherwise identical to @ref tcp_socket::wait. 275   dispatch. Otherwise identical to @ref tcp_socket::wait.
282   276  
283   @param w The wait direction (read, write, or error). 277   @param w The wait direction (read, write, or error).
284   278  
285   @return An awaitable yielding `io_result<>`. 279   @return An awaitable yielding `io_result<>`.
286   */ 280   */
HITCBC 287   10 [[nodiscard]] auto wait(wait_type w) 281   10 [[nodiscard]] auto wait(wait_type w)
288   { 282   {
HITCBC 289   10 return native_wait_awaitable(*this, w); 283   10 return native_wait_awaitable(*this, w);
290   } 284   }
291   }; 285   };
292   286  
293   } // namespace boost::corosio 287   } // namespace boost::corosio
294   288  
295   #endif 289   #endif