include/boost/corosio/native/native_tcp_socket.hpp

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