include/boost/corosio/native/native_tcp_socket.hpp

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