include/boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp

86.5% Lines (540/624) 97.7% List of functions (84/86) 65.0% Branches (158/243)
f(x) Functions (86)
Function Calls Lines Branches Blocks
boost::corosio::detail::connect_op::connect_op(boost::corosio::detail::win_tcp_socket_internal&) :76 3772x 100.0% 100.0% boost::corosio::detail::read_op::read_op(boost::corosio::detail::win_tcp_socket_internal&) :83 3772x 100.0% 100.0% boost::corosio::detail::write_op::write_op(boost::corosio::detail::win_tcp_socket_internal&) :90 3772x 100.0% 100.0% boost::corosio::detail::accept_op::accept_op() :97 1180x 100.0% 100.0% boost::corosio::detail::connect_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :105 0 0.0% 0.0% 0.0% boost::corosio::detail::read_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :116 800x 100.0% 50.0% 88.9% boost::corosio::detail::write_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :128 2x 100.0% 50.0% 88.9% boost::corosio::detail::accept_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :140 6x 100.0% 50.0% 83.3% boost::corosio::detail::accept_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :152 1227x 83.0% 66.7% 80.9% boost::corosio::detail::connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :251 1221x 85.7% 76.5% 82.9% boost::corosio::detail::read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :292 272251x 66.7% 66.7% 61.5% boost::corosio::detail::write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :314 270675x 66.7% 66.7% 61.5% boost::corosio::detail::win_tcp_socket_internal::win_tcp_socket_internal(boost::corosio::detail::win_tcp_service&) :335 3772x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::~win_tcp_socket_internal() :343 3772x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::native_handle() const :349 18849x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::local_endpoint() const :355 19x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::remote_endpoint() const :361 21x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::is_open() const :367 2021x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_socket(unsigned long long) :373 1219x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_endpoints(boost::corosio::endpoint, boost::corosio::endpoint) :379 2438x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::connect(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::endpoint, std::stop_token, std::error_code*) :386 1221x 85.4% 66.7% 65.9% boost::corosio::detail::win_tcp_socket_internal::read_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :464 272251x 97.0% 85.7% 94.7% boost::corosio::detail::win_tcp_socket_internal::write_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :530 270675x 96.7% 78.6% 94.7% boost::corosio::detail::win_tcp_socket_internal::cancel() :592 2140x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::close_socket() :605 11231x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_socket::win_tcp_socket(std::shared_ptr<boost::corosio::detail::win_tcp_socket_internal>) :623 3772x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::close_internal() :630 3772x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket::connect(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::endpoint, std::stop_token, std::error_code*) :640 1221x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::read_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :651 272251x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::write_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :663 270675x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::shutdown(boost::corosio::tcp_socket::shutdown_type) :675 3x 81.2% 66.7% 76.9% boost::corosio::detail::win_tcp_socket::native_handle() const :698 13433x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::set_option(int, int, void const*, unsigned long long) :704 2142x 83.3% 50.0% 75.0% boost::corosio::detail::win_tcp_socket::get_option(int, int, void*, unsigned long long*) const :715 31x 87.5% 50.0% 77.8% boost::corosio::detail::win_tcp_socket::local_endpoint() const :728 19x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::remote_endpoint() const :734 21x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::cancel() :740 2140x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::get_internal() const :746 9897x 100.0% 100.0% boost::corosio::detail::win_tcp_service::win_tcp_service(boost::capy::execution_context&) :753 369x 100.0% 100.0% 65.0% boost::corosio::detail::win_tcp_service::~win_tcp_service() :760 738x 100.0% 100.0% boost::corosio::detail::win_tcp_service::shutdown() :775 369x 55.6% 50.0% 63.6% boost::corosio::detail::win_tcp_service::construct() :798 3772x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_service::destroy(boost::corosio::io_object::implementation*) :818 3772x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::close(boost::corosio::io_object::handle&) :829 6221x 100.0% 100.0% boost::corosio::detail::win_tcp_service::destroy_impl(boost::corosio::detail::win_tcp_socket&) :836 3772x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_impl(boost::corosio::detail::win_tcp_socket_internal&) :846 3772x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_socket(boost::corosio::detail::win_tcp_socket_internal&, int, int, int) :853 1238x 76.5% 71.4% 68.8% boost::corosio::detail::win_tcp_service::native_handle() const :888 1227x 100.0% 100.0% boost::corosio::detail::win_tcp_service::connect_ex() const :894 1221x 100.0% 100.0% boost::corosio::detail::win_tcp_service::accept_ex() const :900 1227x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_pending(boost::corosio::detail::overlapped_op*) :912 545368x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :918 6x 100.0% 100.0% boost::corosio::detail::win_tcp_service::work_started() :924 545374x 100.0% 100.0% boost::corosio::detail::win_tcp_service::load_extension_functions() :936 369x 91.7% 83.3% 88.9% boost::corosio::detail::win_tcp_service::destroy_acceptor_impl(boost::corosio::detail::win_tcp_acceptor&) :962 1180x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_acceptor_impl(boost::corosio::detail::win_tcp_acceptor_internal&) :972 1180x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_acceptor_socket(boost::corosio::detail::win_tcp_acceptor_internal&, int, int, int) :979 1178x 75.0% 71.4% 68.8% boost::corosio::detail::win_tcp_service::bind_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, boost::corosio::endpoint) :1013 1177x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::listen_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, int) :1035 1174x 80.0% 50.0% 66.7% boost::corosio::detail::win_tcp_acceptor_internal::win_tcp_acceptor_internal(boost::corosio::detail::win_tcp_service&) :1047 1180x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::~win_tcp_acceptor_internal() :1052 1180x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::socket_service() :1058 8x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::native_handle() const :1064 1176x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::local_endpoint() const :1070 1165x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::is_open() const :1076 9465x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::set_local_endpoint(boost::corosio::endpoint) :1082 1174x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::accept(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, std::stop_token, std::error_code*, boost::corosio::io_object::implementation**) :1088 1227x 60.8% 51.7% 56.7% boost::corosio::detail::win_tcp_acceptor_internal::cancel() :1181 2x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::close_socket() :1192 4716x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::win_tcp_acceptor(std::shared_ptr<boost::corosio::detail::win_tcp_acceptor_internal>) :1207 1180x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::close_internal() :1214 1180x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor::accept(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, std::stop_token, std::error_code*, boost::corosio::io_object::implementation**) :1224 1227x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::local_endpoint() const :1235 1165x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::is_open() const :1241 9465x 100.0% 75.0% 100.0% boost::corosio::detail::win_tcp_acceptor::cancel() :1247 2x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::set_option(int, int, void const*, unsigned long long) :1253 1176x 83.3% 50.0% 75.0% boost::corosio::detail::win_tcp_acceptor::get_option(int, int, void*, unsigned long long*) const :1264 0 0.0% 0.0% 0.0% boost::corosio::detail::win_tcp_acceptor::get_internal() const :1277 5887x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::win_tcp_acceptor_service(boost::capy::execution_context&, boost::corosio::detail::win_tcp_service&) :1284 369x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::construct() :1292 1180x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_acceptor_service::destroy(boost::corosio::io_object::implementation*) :1312 1180x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::close(boost::corosio::io_object::handle&) :1323 2358x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::open_acceptor_socket(boost::corosio::tcp_acceptor::implementation&, int, int, int) :1330 1178x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::bind_acceptor(boost::corosio::tcp_acceptor::implementation&, boost::corosio::endpoint) :1339 1177x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::listen_acceptor(boost::corosio::tcp_acceptor::implementation&, int) :1347 1174x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::shutdown() :1355 369x 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
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_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_HAS_IOCP
17
18 #include <boost/corosio/detail/config.hpp>
19 #include <boost/corosio/detail/except.hpp>
20 #include <boost/capy/ex/execution_context.hpp>
21
22 #include <boost/corosio/native/detail/iocp/win_tcp_acceptor.hpp>
23 #include <boost/corosio/native/detail/iocp/win_tcp_service.hpp>
24
25 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
26 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
27
28 #include <boost/corosio/native/detail/endpoint_convert.hpp>
29 #include <boost/corosio/native/detail/make_err.hpp>
30 #include <boost/corosio/detail/dispatch_coro.hpp>
31
32 #include <Ws2tcpip.h>
33
34 namespace boost::corosio::detail {
35
36 /** IOCP acceptor service wrapping win_tcp_service for acceptor lifecycle.
37
38 Provides io_service + acceptor_service interface for tcp_acceptor
39 on Windows. Delegates to win_tcp_service for actual socket operations.
40 */
41 class BOOST_COROSIO_DECL win_tcp_acceptor_service final
42 : public capy::execution_context::service
43 , public io_object::io_service
44 {
45 public:
46 using key_type = win_tcp_acceptor_service;
47
48 win_tcp_acceptor_service(capy::execution_context& ctx, win_tcp_service& svc);
49
50 io_object::implementation* construct() override;
51
52 void destroy(io_object::implementation* p) override;
53
54 void close(io_object::handle& h) override;
55
56 /** Create the acceptor socket without binding or listening. */
57 std::error_code open_acceptor_socket(
58 tcp_acceptor::implementation& impl, int family, int type, int protocol);
59
60 /** Bind an open acceptor to a local endpoint. */
61 std::error_code
62 bind_acceptor(tcp_acceptor::implementation& impl, endpoint ep);
63
64 /** Start listening for incoming connections. */
65 std::error_code
66 listen_acceptor(tcp_acceptor::implementation& impl, int backlog);
67
68 void shutdown() override;
69
70 private:
71 win_tcp_service& svc_;
72 };
73
74 // Operation constructors
75
76 3772x inline connect_op::connect_op(win_tcp_socket_internal& internal_) noexcept
77 : overlapped_op(&do_complete)
78 3772x , internal(internal_)
79 {
80 3772x cancel_func_ = &do_cancel_impl;
81 3772x }
82
83 3772x inline read_op::read_op(win_tcp_socket_internal& internal_) noexcept
84 : overlapped_op(&do_complete)
85 3772x , internal(internal_)
86 {
87 3772x cancel_func_ = &do_cancel_impl;
88 3772x }
89
90 3772x inline write_op::write_op(win_tcp_socket_internal& internal_) noexcept
91 : overlapped_op(&do_complete)
92 3772x , internal(internal_)
93 {
94 3772x cancel_func_ = &do_cancel_impl;
95 3772x }
96
97 1180x inline accept_op::accept_op() noexcept : overlapped_op(&do_complete)
98 {
99 1180x cancel_func_ = &do_cancel_impl;
100 1180x }
101
102 // Cancellation functions
103
104 inline void
105 connect_op::do_cancel_impl(overlapped_op* base) noexcept
106 {
107 auto* op = static_cast<connect_op*>(base);
108 if (op->internal.is_open())
109 {
110 ::CancelIoEx(
111 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
112 }
113 }
114
115 inline void
116 800x read_op::do_cancel_impl(overlapped_op* base) noexcept
117 {
118 800x auto* op = static_cast<read_op*>(base);
119 800x op->cancelled.store(true, std::memory_order_release);
120
1/2
✓ Branch 4 → 5 taken 800 times.
✗ Branch 4 → 10 not taken.
800x if (op->internal.is_open())
121 {
122
1/2
✓ Branch 5 → 6 taken 800 times.
✗ Branch 5 → 7 not taken.
1600x ::CancelIoEx(
123 800x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
124 }
125 800x }
126
127 inline void
128 2x write_op::do_cancel_impl(overlapped_op* base) noexcept
129 {
130 2x auto* op = static_cast<write_op*>(base);
131 2x op->cancelled.store(true, std::memory_order_release);
132
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 10 not taken.
2x if (op->internal.is_open())
133 {
134
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
4x ::CancelIoEx(
135 2x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
136 }
137 2x }
138
139 inline void
140 6x accept_op::do_cancel_impl(overlapped_op* base) noexcept
141 {
142 6x auto* op = static_cast<accept_op*>(base);
143
1/2
✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 7 not taken.
6x if (op->listen_socket != INVALID_SOCKET)
144 {
145
1/2
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 5 not taken.
6x ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
146 }
147 6x }
148
149 // accept_op completion handler
150
151 inline void
152 1227x accept_op::do_complete(
153 void* owner,
154 scheduler_op* base,
155 std::uint32_t /*bytes*/,
156 std::uint32_t /*error*/)
157 {
158 1227x auto* op = static_cast<accept_op*>(base);
159
160
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 1227 times.
1227x if (!owner)
161 {
162 if (op->accepted_socket != INVALID_SOCKET)
163 {
164 ::closesocket(op->accepted_socket);
165 op->accepted_socket = INVALID_SOCKET;
166 }
167
168 if (op->peer_wrapper)
169 {
170 op->peer_wrapper->close_internal();
171 op->peer_wrapper = nullptr;
172 }
173
174 op->cleanup_only();
175 op->acceptor_ptr.reset();
176 return;
177 }
178
179 1227x op->stop_cb.reset();
180
181 bool success =
182
3/4
✓ Branch 13 → 14 taken 1219 times.
✓ Branch 13 → 17 taken 8 times.
✓ Branch 15 → 16 taken 1219 times.
✗ Branch 15 → 17 not taken.
1227x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
183
184
1/2
✓ Branch 18 → 19 taken 1227 times.
✗ Branch 18 → 27 not taken.
1227x if (op->ec_out)
185 {
186
2/2
✓ Branch 20 → 21 taken 7 times.
✓ Branch 20 → 23 taken 1220 times.
1227x if (op->cancelled.load(std::memory_order_acquire))
187 7x *op->ec_out = capy::error::canceled;
188
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 1219 times.
1220x else if (op->dwError != 0)
189 1x *op->ec_out = make_err(op->dwError);
190 else
191 1219x *op->ec_out = {};
192 }
193
194
4/6
✓ Branch 27 → 28 taken 1219 times.
✓ Branch 27 → 48 taken 8 times.
✓ Branch 28 → 29 taken 1219 times.
✗ Branch 28 → 48 not taken.
✓ Branch 29 → 30 taken 1219 times.
✗ Branch 29 → 48 not taken.
1227x if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
195 {
196 1219x ::setsockopt(
197 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
198
1/1
✓ Branch 30 → 31 taken 1219 times.
1219x reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
199
200 1219x op->peer_wrapper->get_internal()->set_socket(op->accepted_socket);
201
202 1219x sockaddr_storage local_storage{};
203 1219x int local_len = sizeof(local_storage);
204 1219x sockaddr_storage remote_storage{};
205 1219x int remote_len = sizeof(remote_storage);
206
207 1219x endpoint local_ep, remote_ep;
208
1/1
✓ Branch 35 → 36 taken 1219 times.
1219x if (::getsockname(
209 op->accepted_socket,
210
2/2
✓ Branch 36 → 37 taken 1218 times.
✓ Branch 36 → 39 taken 1 time.
1219x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
211 1218x local_ep = from_sockaddr(local_storage);
212
1/1
✓ Branch 39 → 40 taken 1219 times.
1219x if (::getpeername(
213 op->accepted_socket,
214
2/2
✓ Branch 40 → 41 taken 1218 times.
✓ Branch 40 → 43 taken 1 time.
1219x reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) == 0)
215 1218x remote_ep = from_sockaddr(remote_storage);
216
217 1219x op->peer_wrapper->get_internal()->set_endpoints(local_ep, remote_ep);
218 1219x op->accepted_socket = INVALID_SOCKET;
219
220
1/2
✓ Branch 45 → 46 taken 1219 times.
✗ Branch 45 → 47 not taken.
1219x if (op->impl_out)
221 1219x *op->impl_out = op->peer_wrapper;
222 1219x }
223 else
224 {
225
1/2
✓ Branch 48 → 49 taken 8 times.
✗ Branch 48 → 51 not taken.
8x if (op->accepted_socket != INVALID_SOCKET)
226 {
227
1/1
✓ Branch 49 → 50 taken 8 times.
8x ::closesocket(op->accepted_socket);
228 8x op->accepted_socket = INVALID_SOCKET;
229 }
230
231
1/2
✓ Branch 51 → 52 taken 8 times.
✗ Branch 51 → 56 not taken.
8x if (op->peer_wrapper)
232 {
233
1/1
✓ Branch 54 → 55 taken 8 times.
8x op->acceptor_ptr->socket_service().destroy(op->peer_wrapper);
234 8x op->peer_wrapper = nullptr;
235 }
236
237
1/2
✓ Branch 56 → 57 taken 8 times.
✗ Branch 56 → 58 not taken.
8x if (op->impl_out)
238 8x *op->impl_out = nullptr;
239 }
240
241 1227x auto saved_h = op->h;
242 1227x auto saved_ex = op->ex;
243 1227x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
244
245
2/2
✓ Branch 60 → 61 taken 1227 times.
✓ Branch 61 → 62 taken 1227 times.
1227x dispatch_coro(saved_ex, saved_h).resume();
246 1227x }
247
248 // connect_op completion handler
249
250 inline void
251 1221x connect_op::do_complete(
252 void* owner,
253 scheduler_op* base,
254 std::uint32_t /*bytes*/,
255 std::uint32_t /*error*/)
256 {
257 1221x auto* op = static_cast<connect_op*>(base);
258
259
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1221 times.
1221x if (!owner)
260 {
261 op->cleanup_only();
262 op->internal_ptr.reset();
263 return;
264 }
265
266 bool success =
267
3/4
✓ Branch 6 → 7 taken 1219 times.
✓ Branch 6 → 10 taken 2 times.
✓ Branch 8 → 9 taken 1219 times.
✗ Branch 8 → 10 not taken.
1221x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
268
5/6
✓ Branch 11 → 12 taken 1219 times.
✓ Branch 11 → 15 taken 2 times.
✓ Branch 13 → 14 taken 1219 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 1219 times.
✓ Branch 16 → 27 taken 2 times.
1221x if (success && op->internal.is_open())
269 {
270 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
271
1/1
✓ Branch 18 → 19 taken 1219 times.
1219x ::setsockopt(
272 1219x op->internal.native_handle(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
273 nullptr, 0);
274
275 1219x endpoint local_ep;
276 1219x sockaddr_storage local_storage{};
277 1219x int local_len = sizeof(local_storage);
278
1/1
✓ Branch 21 → 22 taken 1219 times.
1219x if (::getsockname(
279 1219x op->internal.native_handle(),
280
1/2
✓ Branch 22 → 23 taken 1219 times.
✗ Branch 22 → 25 not taken.
1219x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
281 1219x local_ep = from_sockaddr(local_storage);
282 1219x op->internal.set_endpoints(local_ep, op->target_endpoint);
283 }
284
285 1221x auto prevent_premature_destruction = std::move(op->internal_ptr);
286
1/1
✓ Branch 29 → 30 taken 1221 times.
1221x op->invoke_handler();
287 1221x }
288
289 // read_op completion handler
290
291 inline void
292 272251x read_op::do_complete(
293 void* owner,
294 scheduler_op* base,
295 std::uint32_t /*bytes*/,
296 std::uint32_t /*error*/)
297 {
298 272251x auto* op = static_cast<read_op*>(base);
299
300
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 272251 times.
272251x if (!owner)
301 {
302 op->cleanup_only();
303 op->internal_ptr.reset();
304 return;
305 }
306
307 272251x auto prevent_premature_destruction = std::move(op->internal_ptr);
308
1/1
✓ Branch 8 → 9 taken 272251 times.
272251x op->invoke_handler();
309 272251x }
310
311 // write_op completion handler
312
313 inline void
314 270675x write_op::do_complete(
315 void* owner,
316 scheduler_op* base,
317 std::uint32_t /*bytes*/,
318 std::uint32_t /*error*/)
319 {
320 270675x auto* op = static_cast<write_op*>(base);
321
322
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 270675 times.
270675x if (!owner)
323 {
324 op->cleanup_only();
325 op->internal_ptr.reset();
326 return;
327 }
328
329 270675x auto prevent_premature_destruction = std::move(op->internal_ptr);
330
1/1
✓ Branch 8 → 9 taken 270675 times.
270675x op->invoke_handler();
331 270675x }
332
333 // win_tcp_socket_internal
334
335 3772x inline win_tcp_socket_internal::win_tcp_socket_internal(win_tcp_service& svc) noexcept
336 3772x : svc_(svc)
337 3772x , conn_(*this)
338 3772x , rd_(*this)
339 7544x , wr_(*this)
340 {
341 3772x }
342
343 3772x inline win_tcp_socket_internal::~win_tcp_socket_internal()
344 {
345 3772x svc_.unregister_impl(*this);
346 3772x }
347
348 inline SOCKET
349 18849x win_tcp_socket_internal::native_handle() const noexcept
350 {
351 18849x return socket_;
352 }
353
354 inline endpoint
355 19x win_tcp_socket_internal::local_endpoint() const noexcept
356 {
357 19x return local_endpoint_;
358 }
359
360 inline endpoint
361 21x win_tcp_socket_internal::remote_endpoint() const noexcept
362 {
363 21x return remote_endpoint_;
364 }
365
366 inline bool
367 2021x win_tcp_socket_internal::is_open() const noexcept
368 {
369 2021x return socket_ != INVALID_SOCKET;
370 }
371
372 inline void
373 1219x win_tcp_socket_internal::set_socket(SOCKET s) noexcept
374 {
375 1219x socket_ = s;
376 1219x }
377
378 inline void
379 2438x win_tcp_socket_internal::set_endpoints(endpoint local, endpoint remote) noexcept
380 {
381 2438x local_endpoint_ = local;
382 2438x remote_endpoint_ = remote;
383 2438x }
384
385 inline std::coroutine_handle<>
386 1221x win_tcp_socket_internal::connect(
387 std::coroutine_handle<> h,
388 capy::executor_ref d,
389 endpoint ep,
390 std::stop_token token,
391 std::error_code* ec)
392 {
393 // Keep internal alive during I/O
394
1/1
✓ Branch 2 → 3 taken 1221 times.
1221x conn_.internal_ptr = shared_from_this();
395
396 1221x auto& op = conn_;
397 1221x op.reset();
398 1221x op.h = h;
399 1221x op.ex = d;
400 1221x op.ec_out = ec;
401 1221x op.target_endpoint = ep;
402 1221x op.start(token);
403
404 1221x svc_.work_started();
405
406 // Ephemeral bind — must match the socket's family, not the endpoint's
407 1221x sockaddr_storage bind_storage{};
408 socklen_t bind_len;
409
2/2
✓ Branch 10 → 11 taken 4 times.
✓ Branch 10 → 12 taken 1217 times.
1221x if (family_ == AF_INET6)
410 {
411 4x sockaddr_in6 sa6{};
412 4x sa6.sin6_family = AF_INET6;
413 4x sa6.sin6_port = 0;
414 4x sa6.sin6_addr = in6addr_any;
415 4x std::memcpy(&bind_storage, &sa6, sizeof(sa6));
416 4x bind_len = sizeof(sa6);
417 }
418 else
419 {
420 1217x sockaddr_in sa4{};
421 1217x sa4.sin_family = AF_INET;
422 1217x sa4.sin_addr.s_addr = INADDR_ANY;
423 1217x sa4.sin_port = 0;
424 1217x std::memcpy(&bind_storage, &sa4, sizeof(sa4));
425 1217x bind_len = sizeof(sa4);
426 }
427
428
2/3
✓ Branch 13 → 14 taken 1221 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 20 taken 1221 times.
1221x if (::bind(socket_, reinterpret_cast<sockaddr*>(&bind_storage), bind_len) ==
429 SOCKET_ERROR)
430 {
431 svc_.on_completion(&op, ::WSAGetLastError(), 0);
432 return std::noop_coroutine();
433 }
434
435 1221x auto connect_ex = svc_.connect_ex();
436
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 26 taken 1221 times.
1221x if (!connect_ex)
437 {
438 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
439 return std::noop_coroutine();
440 }
441
442 1221x sockaddr_storage storage{};
443 1221x socklen_t addrlen = detail::to_sockaddr(ep, family_, storage);
444
445
1/1
✓ Branch 27 → 28 taken 1221 times.
1221x BOOL result = connect_ex(
446 socket_, reinterpret_cast<sockaddr*>(&storage),
447 static_cast<int>(addrlen), nullptr, 0, nullptr, &op);
448
449
1/2
✓ Branch 28 → 29 taken 1221 times.
✗ Branch 28 → 35 not taken.
1221x if (!result)
450 {
451
1/1
✓ Branch 29 → 30 taken 1221 times.
1221x DWORD err = ::WSAGetLastError();
452
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 1221 times.
1221x if (err != ERROR_IO_PENDING)
453 {
454 svc_.on_completion(&op, err, 0);
455 return std::noop_coroutine();
456 }
457 }
458
459 1221x svc_.on_pending(&op);
460 1221x return std::noop_coroutine();
461 }
462
463 inline std::coroutine_handle<>
464 272251x win_tcp_socket_internal::read_some(
465 std::coroutine_handle<> h,
466 capy::executor_ref d,
467 buffer_param param,
468 std::stop_token token,
469 std::error_code* ec,
470 std::size_t* bytes_out)
471 {
472 // Keep internal alive during I/O
473
1/1
✓ Branch 2 → 3 taken 272251 times.
272251x rd_.internal_ptr = shared_from_this();
474
475 272251x auto& op = rd_;
476 272251x op.reset();
477 272251x op.is_read_ = true;
478 272251x op.h = h;
479 272251x op.ex = d;
480 272251x op.ec_out = ec;
481 272251x op.bytes_out = bytes_out;
482 272251x op.start(token);
483
484 272251x svc_.work_started();
485
486 // Prepare buffers
487 272251x capy::mutable_buffer bufs[read_op::max_buffers];
488 272251x op.wsabuf_count =
489 272251x static_cast<DWORD>(param.copy_to(bufs, read_op::max_buffers));
490
491 // Handle empty buffer: complete with 0 bytes
492
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 272250 times.
272251x if (op.wsabuf_count == 0)
493 {
494 1x op.empty_buffer = true;
495 1x svc_.on_completion(&op, 0, 0);
496 1x return std::noop_coroutine();
497 }
498
499
2/2
✓ Branch 20 → 17 taken 272250 times.
✓ Branch 20 → 21 taken 272250 times.
544500x for (DWORD i = 0; i < op.wsabuf_count; ++i)
500 {
501 272250x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
502 272250x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
503 }
504
505 272250x op.flags = 0;
506
507 544500x int result = ::WSARecv(
508
1/1
✓ Branch 21 → 22 taken 272250 times.
272250x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
509
510
2/2
✓ Branch 22 → 23 taken 3406 times.
✓ Branch 22 → 29 taken 268844 times.
272250x if (result == SOCKET_ERROR)
511 {
512
1/1
✓ Branch 23 → 24 taken 3406 times.
3406x DWORD err = ::WSAGetLastError();
513
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 24 → 29 taken 3403 times.
3406x if (err != WSA_IO_PENDING)
514 {
515 3x svc_.on_completion(&op, err, 0);
516 3x return std::noop_coroutine();
517 }
518 }
519
520 272247x svc_.on_pending(&op);
521
522 // Re-check cancellation after I/O is pending
523
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 272247 times.
272247x if (op.cancelled.load(std::memory_order_acquire))
524 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
525
526 272247x return std::noop_coroutine();
527 }
528
529 inline std::coroutine_handle<>
530 270675x win_tcp_socket_internal::write_some(
531 std::coroutine_handle<> h,
532 capy::executor_ref d,
533 buffer_param param,
534 std::stop_token token,
535 std::error_code* ec,
536 std::size_t* bytes_out)
537 {
538 // Keep internal alive during I/O
539
1/1
✓ Branch 2 → 3 taken 270675 times.
270675x wr_.internal_ptr = shared_from_this();
540
541 270675x auto& op = wr_;
542 270675x op.reset();
543 270675x op.h = h;
544 270675x op.ex = d;
545 270675x op.ec_out = ec;
546 270675x op.bytes_out = bytes_out;
547 270675x op.start(token);
548
549 270675x svc_.work_started();
550
551 // Prepare buffers
552 270675x capy::mutable_buffer bufs[write_op::max_buffers];
553 270675x op.wsabuf_count =
554 270675x static_cast<DWORD>(param.copy_to(bufs, write_op::max_buffers));
555
556 // Handle empty buffer: complete immediately with 0 bytes
557
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 270674 times.
270675x if (op.wsabuf_count == 0)
558 {
559 1x svc_.on_completion(&op, 0, 0);
560 1x return std::noop_coroutine();
561 }
562
563
2/2
✓ Branch 20 → 17 taken 270674 times.
✓ Branch 20 → 21 taken 270674 times.
541348x for (DWORD i = 0; i < op.wsabuf_count; ++i)
564 {
565 270674x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
566 270674x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
567 }
568
569 541348x int result = ::WSASend(
570
1/1
✓ Branch 21 → 22 taken 270674 times.
270674x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
571
572
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 29 taken 270673 times.
270674x if (result == SOCKET_ERROR)
573 {
574
1/1
✓ Branch 23 → 24 taken 1 time.
1x DWORD err = ::WSAGetLastError();
575
1/2
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 29 not taken.
1x if (err != WSA_IO_PENDING)
576 {
577 1x svc_.on_completion(&op, err, 0);
578 1x return std::noop_coroutine();
579 }
580 }
581
582 270673x svc_.on_pending(&op);
583
584 // Re-check cancellation after I/O is pending
585
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 270673 times.
270673x if (op.cancelled.load(std::memory_order_acquire))
586 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
587
588 270673x return std::noop_coroutine();
589 }
590
591 inline void
592 2140x win_tcp_socket_internal::cancel() noexcept
593 {
594
1/2
✓ Branch 2 → 3 taken 2140 times.
✗ Branch 2 → 4 not taken.
2140x if (socket_ != INVALID_SOCKET)
595 {
596 2140x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
597 }
598
599 2140x conn_.request_cancel();
600 2140x rd_.request_cancel();
601 2140x wr_.request_cancel();
602 2140x }
603
604 inline void
605 11231x win_tcp_socket_internal::close_socket() noexcept
606 {
607
2/2
✓ Branch 2 → 3 taken 2457 times.
✓ Branch 2 → 6 taken 8774 times.
11231x if (socket_ != INVALID_SOCKET)
608 {
609 2457x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
610 2457x ::closesocket(socket_);
611 2457x socket_ = INVALID_SOCKET;
612 }
613
614 11231x family_ = AF_UNSPEC;
615
616 // Clear cached endpoints
617 11231x local_endpoint_ = endpoint{};
618 11231x remote_endpoint_ = endpoint{};
619 11231x }
620
621 // win_tcp_socket
622
623 3772x inline win_tcp_socket::win_tcp_socket(
624 3772x std::shared_ptr<win_tcp_socket_internal> internal) noexcept
625 3772x : internal_(std::move(internal))
626 {
627 3772x }
628
629 inline void
630 3772x win_tcp_socket::close_internal() noexcept
631 {
632
1/2
✓ Branch 3 → 4 taken 3772 times.
✗ Branch 3 → 7 not taken.
3772x if (internal_)
633 {
634 3772x internal_->close_socket();
635 3772x internal_.reset();
636 }
637 3772x }
638
639 inline std::coroutine_handle<>
640 1221x win_tcp_socket::connect(
641 std::coroutine_handle<> h,
642 capy::executor_ref d,
643 endpoint ep,
644 std::stop_token token,
645 std::error_code* ec)
646 {
647
1/1
✓ Branch 4 → 5 taken 1221 times.
1221x return internal_->connect(h, d, ep, token, ec);
648 }
649
650 inline std::coroutine_handle<>
651 272251x win_tcp_socket::read_some(
652 std::coroutine_handle<> h,
653 capy::executor_ref d,
654 buffer_param buf,
655 std::stop_token token,
656 std::error_code* ec,
657 std::size_t* bytes)
658 {
659
1/1
✓ Branch 4 → 5 taken 272251 times.
272251x return internal_->read_some(h, d, buf, token, ec, bytes);
660 }
661
662 inline std::coroutine_handle<>
663 270675x win_tcp_socket::write_some(
664 std::coroutine_handle<> h,
665 capy::executor_ref d,
666 buffer_param buf,
667 std::stop_token token,
668 std::error_code* ec,
669 std::size_t* bytes)
670 {
671
1/1
✓ Branch 4 → 5 taken 270675 times.
270675x return internal_->write_some(h, d, buf, token, ec, bytes);
672 }
673
674 inline std::error_code
675 3x win_tcp_socket::shutdown(tcp_socket::shutdown_type what) noexcept
676 {
677 int how;
678
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 1 time.
✗ Branch 2 → 6 not taken.
3x switch (what)
679 {
680 1x case tcp_socket::shutdown_receive:
681 1x how = SD_RECEIVE;
682 1x break;
683 1x case tcp_socket::shutdown_send:
684 1x how = SD_SEND;
685 1x break;
686 1x case tcp_socket::shutdown_both:
687 1x how = SD_BOTH;
688 1x break;
689 default:
690 return make_err(WSAEINVAL);
691 }
692
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 3 times.
3x if (::shutdown(internal_->native_handle(), how) != 0)
693 return make_err(WSAGetLastError());
694 3x return {};
695 }
696
697 inline native_handle_type
698 13433x win_tcp_socket::native_handle() const noexcept
699 {
700 13433x return static_cast<native_handle_type>(internal_->native_handle());
701 }
702
703 inline std::error_code
704 2142x win_tcp_socket::set_option(
705 int level, int optname, void const* data, std::size_t size) noexcept
706 {
707 2142x if (::setsockopt(
708 2142x internal_->native_handle(), level, optname,
709
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 2142 times.
2142x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
710 return make_err(WSAGetLastError());
711 2142x return {};
712 }
713
714 inline std::error_code
715 31x win_tcp_socket::get_option(
716 int level, int optname, void* data, std::size_t* size) const noexcept
717 {
718 31x int len = static_cast<int>(*size);
719 31x if (::getsockopt(
720 31x internal_->native_handle(), level, optname,
721
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 31 times.
31x reinterpret_cast<char*>(data), &len) != 0)
722 return make_err(WSAGetLastError());
723 31x *size = static_cast<std::size_t>(len);
724 31x return {};
725 }
726
727 inline endpoint
728 19x win_tcp_socket::local_endpoint() const noexcept
729 {
730 19x return internal_->local_endpoint();
731 }
732
733 inline endpoint
734 21x win_tcp_socket::remote_endpoint() const noexcept
735 {
736 21x return internal_->remote_endpoint();
737 }
738
739 inline void
740 2140x win_tcp_socket::cancel() noexcept
741 {
742 2140x internal_->cancel();
743 2140x }
744
745 inline win_tcp_socket_internal*
746 9897x win_tcp_socket::get_internal() const noexcept
747 {
748 9897x return internal_.get();
749 }
750
751 // win_tcp_service
752
753 369x inline win_tcp_service::win_tcp_service(capy::execution_context& ctx)
754 738x : sched_(ctx.use_service<win_scheduler>())
755
2/2
✓ Branch 5 → 6 taken 369 times.
✓ Branch 6 → 7 taken 369 times.
369x , iocp_(sched_.native_handle())
756 {
757
1/1
✓ Branch 12 → 13 taken 369 times.
369x load_extension_functions();
758 369x }
759
760 738x inline win_tcp_service::~win_tcp_service()
761 {
762 // Delete wrappers that survived shutdown. This runs after
763 // win_scheduler is destroyed (reverse creation order), so
764 // all coroutine frames and their tcp_socket members are gone.
765
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 369 times.
369x for (auto* w = socket_wrapper_list_.pop_front(); w != nullptr;
766 w = socket_wrapper_list_.pop_front())
767 delete w;
768
769
1/2
✗ Branch 11 → 8 not taken.
✓ Branch 11 → 12 taken 369 times.
369x for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
770 w = acceptor_wrapper_list_.pop_front())
771 delete w;
772 738x }
773
774 inline void
775 369x win_tcp_service::shutdown()
776 {
777 369x std::lock_guard<win_mutex> lock(mutex_);
778
779 // Close all sockets to force pending I/O to complete via IOCP.
780 // Wrappers are NOT deleted here - coroutine frames destroyed
781 // during scheduler shutdown may still hold tcp_socket objects
782 // that reference them. Wrapper deletion is deferred to ~win_tcp_service
783 // after the scheduler has drained all outstanding operations.
784
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 369 times.
369x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
785 impl = socket_list_.pop_front())
786 {
787 impl->close_socket();
788 }
789
790
1/2
✗ Branch 10 → 8 not taken.
✓ Branch 10 → 11 taken 369 times.
369x for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
791 impl = acceptor_list_.pop_front())
792 {
793 impl->close_socket();
794 }
795 369x }
796
797 inline io_object::implementation*
798 3772x win_tcp_service::construct()
799 {
800
1/1
✓ Branch 2 → 3 taken 3772 times.
3772x auto internal = std::make_shared<win_tcp_socket_internal>(*this);
801
802 {
803 3772x std::lock_guard<win_mutex> lock(mutex_);
804 3772x socket_list_.push_back(internal.get());
805 3772x }
806
807
1/1
✓ Branch 7 → 8 taken 3772 times.
3772x auto* wrapper = new win_tcp_socket(std::move(internal));
808
809 {
810 3772x std::lock_guard<win_mutex> lock(mutex_);
811 3772x socket_wrapper_list_.push_back(wrapper);
812 3772x }
813
814 3772x return wrapper;
815 3772x }
816
817 inline void
818 3772x win_tcp_service::destroy(io_object::implementation* p)
819 {
820
1/2
✓ Branch 2 → 3 taken 3772 times.
✗ Branch 2 → 5 not taken.
3772x if (p)
821 {
822 3772x auto& wrapper = static_cast<win_tcp_socket&>(*p);
823 3772x wrapper.close_internal();
824 3772x destroy_impl(wrapper);
825 }
826 3772x }
827
828 inline void
829 6221x win_tcp_service::close(io_object::handle& h)
830 {
831 6221x auto& wrapper = static_cast<win_tcp_socket&>(*h.get());
832 6221x wrapper.get_internal()->close_socket();
833 6221x }
834
835 inline void
836 3772x win_tcp_service::destroy_impl(win_tcp_socket& impl)
837 {
838 {
839 3772x std::lock_guard<win_mutex> lock(mutex_);
840 3772x socket_wrapper_list_.remove(&impl);
841 3772x }
842
1/2
✓ Branch 5 → 6 taken 3772 times.
✗ Branch 5 → 7 not taken.
3772x delete &impl;
843 3772x }
844
845 inline void
846 3772x win_tcp_service::unregister_impl(win_tcp_socket_internal& impl)
847 {
848 3772x std::lock_guard<win_mutex> lock(mutex_);
849 3772x socket_list_.remove(&impl);
850 3772x }
851
852 inline std::error_code
853 1238x win_tcp_service::open_socket(
854 win_tcp_socket_internal& impl, int family, int type, int protocol)
855 {
856 1238x impl.close_socket();
857
858 SOCKET sock =
859 1238x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
860
861
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1238 times.
1238x if (sock == INVALID_SOCKET)
862 return make_err(::WSAGetLastError());
863
864
2/2
✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 10 taken 1233 times.
1238x if (family == AF_INET6)
865 {
866 5x DWORD one = 1;
867
1/1
✓ Branch 8 → 9 taken 5 times.
5x ::setsockopt(
868 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
869 sizeof(one));
870 }
871
872 2476x HANDLE result = ::CreateIoCompletionPort(
873 1238x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
874
875
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1238 times.
1238x if (result == nullptr)
876 {
877 DWORD dwError = ::GetLastError();
878 ::closesocket(sock);
879 return make_err(dwError);
880 }
881
882 1238x impl.socket_ = sock;
883 1238x impl.family_ = family;
884 1238x return {};
885 }
886
887 inline void*
888 1227x win_tcp_service::native_handle() const noexcept
889 {
890 1227x return iocp_;
891 }
892
893 inline LPFN_CONNECTEX
894 1221x win_tcp_service::connect_ex() const noexcept
895 {
896 1221x return connect_ex_;
897 }
898
899 inline LPFN_ACCEPTEX
900 1227x win_tcp_service::accept_ex() const noexcept
901 {
902 1227x return accept_ex_;
903 }
904
905 inline void
906 win_tcp_service::post(overlapped_op* op)
907 {
908 sched_.post(op);
909 }
910
911 inline void
912 545368x win_tcp_service::on_pending(overlapped_op* op) noexcept
913 {
914 545368x sched_.on_pending(op);
915 545368x }
916
917 inline void
918 6x win_tcp_service::on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept
919 {
920 6x sched_.on_completion(op, error, bytes);
921 6x }
922
923 inline void
924 545374x win_tcp_service::work_started() noexcept
925 {
926 545374x sched_.work_started();
927 545374x }
928
929 inline void
930 win_tcp_service::work_finished() noexcept
931 {
932 sched_.work_finished();
933 }
934
935 inline void
936 369x win_tcp_service::load_extension_functions()
937 {
938
1/1
✓ Branch 2 → 3 taken 369 times.
369x SOCKET sock = ::WSASocketW(
939 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
940
941
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 369 times.
369x if (sock == INVALID_SOCKET)
942 return;
943
944 369x DWORD bytes = 0;
945
946 369x GUID connect_ex_guid = WSAID_CONNECTEX;
947 369x ::WSAIoctl(
948 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
949
1/1
✓ Branch 5 → 6 taken 369 times.
369x sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
950 nullptr, nullptr);
951
952 369x GUID accept_ex_guid = WSAID_ACCEPTEX;
953 369x ::WSAIoctl(
954 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
955
1/1
✓ Branch 6 → 7 taken 369 times.
369x sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
956 nullptr, nullptr);
957
958
1/1
✓ Branch 7 → 8 taken 369 times.
369x ::closesocket(sock);
959 }
960
961 inline void
962 1180x win_tcp_service::destroy_acceptor_impl(win_tcp_acceptor& impl)
963 {
964 {
965 1180x std::lock_guard<win_mutex> lock(mutex_);
966 1180x acceptor_wrapper_list_.remove(&impl);
967 1180x }
968
1/2
✓ Branch 5 → 6 taken 1180 times.
✗ Branch 5 → 7 not taken.
1180x delete &impl;
969 1180x }
970
971 inline void
972 1180x win_tcp_service::unregister_acceptor_impl(win_tcp_acceptor_internal& impl)
973 {
974 1180x std::lock_guard<win_mutex> lock(mutex_);
975 1180x acceptor_list_.remove(&impl);
976 1180x }
977
978 inline std::error_code
979 1178x win_tcp_service::open_acceptor_socket(
980 win_tcp_acceptor_internal& impl, int family, int type, int protocol)
981 {
982 1178x impl.close_socket();
983
984 SOCKET sock =
985 1178x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
986
987
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1178 times.
1178x if (sock == INVALID_SOCKET)
988 return make_err(::WSAGetLastError());
989
990
2/2
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 10 taken 1170 times.
1178x if (family == AF_INET6)
991 {
992 8x DWORD val = 0; // dual-stack default
993
1/1
✓ Branch 8 → 9 taken 8 times.
8x ::setsockopt(
994 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&val),
995 sizeof(val));
996 }
997
998 2356x HANDLE result = ::CreateIoCompletionPort(
999 1178x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1000
1001
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1178 times.
1178x if (result == nullptr)
1002 {
1003 DWORD dwError = ::GetLastError();
1004 ::closesocket(sock);
1005 return make_err(dwError);
1006 }
1007
1008 1178x impl.socket_ = sock;
1009 1178x return {};
1010 }
1011
1012 inline std::error_code
1013 1177x win_tcp_service::bind_acceptor(win_tcp_acceptor_internal& impl, endpoint ep)
1014 {
1015 1177x SOCKET sock = impl.socket_;
1016
1017 1177x sockaddr_storage storage{};
1018 1177x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1019
1/1
✓ Branch 3 → 4 taken 1177 times.
1177x if (::bind(
1020 sock, reinterpret_cast<sockaddr*>(&storage),
1021
2/2
✓ Branch 4 → 5 taken 3 times.
✓ Branch 4 → 7 taken 1174 times.
1177x static_cast<int>(addrlen)) == SOCKET_ERROR)
1022
1/1
✓ Branch 5 → 6 taken 3 times.
3x return make_err(::WSAGetLastError());
1023
1024 // Cache local endpoint (resolves ephemeral port)
1025 1174x sockaddr_storage local_storage{};
1026 1174x int local_len = sizeof(local_storage);
1027
1/1
✓ Branch 7 → 8 taken 1174 times.
1174x if (::getsockname(
1028
1/2
✓ Branch 8 → 9 taken 1174 times.
✗ Branch 8 → 11 not taken.
1174x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1029 1174x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1030
1031 1174x return {};
1032 }
1033
1034 inline std::error_code
1035 1174x win_tcp_service::listen_acceptor(win_tcp_acceptor_internal& impl, int backlog)
1036 {
1037 1174x SOCKET sock = impl.socket_;
1038
1039
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1174 times.
1174x if (::listen(sock, backlog) == SOCKET_ERROR)
1040 return make_err(::WSAGetLastError());
1041
1042 1174x return {};
1043 }
1044
1045 // win_tcp_acceptor_internal
1046
1047 1180x inline win_tcp_acceptor_internal::win_tcp_acceptor_internal(win_tcp_service& svc) noexcept
1048 1180x : svc_(svc)
1049 {
1050 1180x }
1051
1052 1180x inline win_tcp_acceptor_internal::~win_tcp_acceptor_internal()
1053 {
1054 1180x svc_.unregister_acceptor_impl(*this);
1055 1180x }
1056
1057 inline win_tcp_service&
1058 8x win_tcp_acceptor_internal::socket_service() noexcept
1059 {
1060 8x return svc_;
1061 }
1062
1063 inline SOCKET
1064 1176x win_tcp_acceptor_internal::native_handle() const noexcept
1065 {
1066 1176x return socket_;
1067 }
1068
1069 inline endpoint
1070 1165x win_tcp_acceptor_internal::local_endpoint() const noexcept
1071 {
1072 1165x return local_endpoint_;
1073 }
1074
1075 inline bool
1076 9465x win_tcp_acceptor_internal::is_open() const noexcept
1077 {
1078 9465x return socket_ != INVALID_SOCKET;
1079 }
1080
1081 inline void
1082 1174x win_tcp_acceptor_internal::set_local_endpoint(endpoint ep) noexcept
1083 {
1084 1174x local_endpoint_ = ep;
1085 1174x }
1086
1087 inline std::coroutine_handle<>
1088 1227x win_tcp_acceptor_internal::accept(
1089 std::coroutine_handle<> h,
1090 capy::executor_ref d,
1091 std::stop_token token,
1092 std::error_code* ec,
1093 io_object::implementation** impl_out)
1094 {
1095 // Keep acceptor internal alive during I/O
1096
1/1
✓ Branch 2 → 3 taken 1227 times.
1227x acc_.acceptor_ptr = shared_from_this();
1097
1098 1227x auto& op = acc_;
1099 1227x op.reset();
1100 1227x op.h = h;
1101 1227x op.ex = d;
1102 1227x op.ec_out = ec;
1103 1227x op.impl_out = impl_out;
1104 1227x op.start(token);
1105
1106 1227x svc_.work_started();
1107
1108 // Create wrapper for the peer socket (service owns it)
1109
1/1
✓ Branch 10 → 11 taken 1227 times.
1227x auto& peer_wrapper = static_cast<win_tcp_socket&>(*svc_.construct());
1110
1111 // Derive AF from the listening socket's cached local endpoint
1112
2/2
✓ Branch 12 → 13 taken 5 times.
✓ Branch 12 → 14 taken 1222 times.
1227x int af = local_endpoint_.is_v6() ? AF_INET6 : AF_INET;
1113
1114 // Create the accepted socket with matching address family
1115
1/1
✓ Branch 15 → 16 taken 1227 times.
1227x SOCKET accepted = ::WSASocketW(
1116 af, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1117
1118
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 23 taken 1227 times.
1227x if (accepted == INVALID_SOCKET)
1119 {
1120 svc_.destroy(&peer_wrapper);
1121 svc_.on_completion(&op, ::WSAGetLastError(), 0);
1122 return std::noop_coroutine();
1123 }
1124
1125
1/1
✓ Branch 24 → 25 taken 1227 times.
1227x HANDLE result = ::CreateIoCompletionPort(
1126 1227x reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
1127
1128
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 33 taken 1227 times.
1227x if (result == nullptr)
1129 {
1130 DWORD err = ::GetLastError();
1131 ::closesocket(accepted);
1132 svc_.destroy(&peer_wrapper);
1133 svc_.on_completion(&op, err, 0);
1134 return std::noop_coroutine();
1135 }
1136
1137 // Set up the accept operation
1138 1227x op.accepted_socket = accepted;
1139 1227x op.peer_wrapper = &peer_wrapper;
1140 1227x op.listen_socket = socket_;
1141
1142 1227x auto accept_ex = svc_.accept_ex();
1143
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 41 taken 1227 times.
1227x if (!accept_ex)
1144 {
1145 ::closesocket(accepted);
1146 svc_.destroy(&peer_wrapper);
1147 op.peer_wrapper = nullptr;
1148 op.accepted_socket = INVALID_SOCKET;
1149 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
1150 return std::noop_coroutine();
1151 }
1152
1153 // AcceptEx address buffer sizes must match the socket's address family
1154
2/2
✓ Branch 41 → 42 taken 5 times.
✓ Branch 41 → 43 taken 1222 times.
1227x DWORD addr_size = static_cast<DWORD>(
1155 (af == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) + 16);
1156 1227x DWORD bytes_received = 0;
1157
1158 2454x BOOL ok = accept_ex(
1159
1/1
✓ Branch 44 → 45 taken 1227 times.
1227x socket_, accepted, op.addr_buf, 0, addr_size, addr_size,
1160 &bytes_received, &op);
1161
1162
1/2
✓ Branch 45 → 46 taken 1227 times.
✗ Branch 45 → 54 not taken.
1227x if (!ok)
1163 {
1164
1/1
✓ Branch 46 → 47 taken 1227 times.
1227x DWORD err = ::WSAGetLastError();
1165
1/2
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 54 taken 1227 times.
1227x if (err != ERROR_IO_PENDING)
1166 {
1167 ::closesocket(accepted);
1168 svc_.destroy(&peer_wrapper);
1169 op.peer_wrapper = nullptr;
1170 op.accepted_socket = INVALID_SOCKET;
1171 svc_.on_completion(&op, err, 0);
1172 return std::noop_coroutine();
1173 }
1174 }
1175
1176 1227x svc_.on_pending(&op);
1177 1227x return std::noop_coroutine();
1178 }
1179
1180 inline void
1181 2x win_tcp_acceptor_internal::cancel() noexcept
1182 {
1183
1/2
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 4 not taken.
2x if (socket_ != INVALID_SOCKET)
1184 {
1185 2x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1186 }
1187
1188 2x acc_.request_cancel();
1189 2x }
1190
1191 inline void
1192 4716x win_tcp_acceptor_internal::close_socket() noexcept
1193 {
1194
2/2
✓ Branch 2 → 3 taken 1178 times.
✓ Branch 2 → 6 taken 3538 times.
4716x if (socket_ != INVALID_SOCKET)
1195 {
1196 1178x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1197 1178x ::closesocket(socket_);
1198 1178x socket_ = INVALID_SOCKET;
1199 }
1200
1201 // Clear cached endpoint
1202 4716x local_endpoint_ = endpoint{};
1203 4716x }
1204
1205 // win_tcp_acceptor
1206
1207 1180x inline win_tcp_acceptor::win_tcp_acceptor(
1208 1180x std::shared_ptr<win_tcp_acceptor_internal> internal) noexcept
1209 1180x : internal_(std::move(internal))
1210 {
1211 1180x }
1212
1213 inline void
1214 1180x win_tcp_acceptor::close_internal() noexcept
1215 {
1216
1/2
✓ Branch 3 → 4 taken 1180 times.
✗ Branch 3 → 7 not taken.
1180x if (internal_)
1217 {
1218 1180x internal_->close_socket();
1219 1180x internal_.reset();
1220 }
1221 1180x }
1222
1223 inline std::coroutine_handle<>
1224 1227x win_tcp_acceptor::accept(
1225 std::coroutine_handle<> h,
1226 capy::executor_ref d,
1227 std::stop_token token,
1228 std::error_code* ec,
1229 io_object::implementation** impl_out)
1230 {
1231
1/1
✓ Branch 4 → 5 taken 1227 times.
1227x return internal_->accept(h, d, token, ec, impl_out);
1232 }
1233
1234 inline endpoint
1235 1165x win_tcp_acceptor::local_endpoint() const noexcept
1236 {
1237 1165x return internal_->local_endpoint();
1238 }
1239
1240 inline bool
1241 9465x win_tcp_acceptor::is_open() const noexcept
1242 {
1243
3/4
✓ Branch 3 → 4 taken 9465 times.
✗ Branch 3 → 8 not taken.
✓ Branch 6 → 7 taken 7118 times.
✓ Branch 6 → 8 taken 2347 times.
9465x return internal_ && internal_->is_open();
1244 }
1245
1246 inline void
1247 2x win_tcp_acceptor::cancel() noexcept
1248 {
1249 2x internal_->cancel();
1250 2x }
1251
1252 inline std::error_code
1253 1176x win_tcp_acceptor::set_option(
1254 int level, int optname, void const* data, std::size_t size) noexcept
1255 {
1256 1176x if (::setsockopt(
1257 1176x internal_->native_handle(), level, optname,
1258
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1176 times.
1176x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
1259 return make_err(WSAGetLastError());
1260 1176x return {};
1261 }
1262
1263 inline std::error_code
1264 win_tcp_acceptor::get_option(
1265 int level, int optname, void* data, std::size_t* size) const noexcept
1266 {
1267 int len = static_cast<int>(*size);
1268 if (::getsockopt(
1269 internal_->native_handle(), level, optname,
1270 reinterpret_cast<char*>(data), &len) != 0)
1271 return make_err(WSAGetLastError());
1272 *size = static_cast<std::size_t>(len);
1273 return {};
1274 }
1275
1276 inline win_tcp_acceptor_internal*
1277 5887x win_tcp_acceptor::get_internal() const noexcept
1278 {
1279 5887x return internal_.get();
1280 }
1281
1282 // win_tcp_acceptor_service
1283
1284 369x inline win_tcp_acceptor_service::win_tcp_acceptor_service(
1285 369x capy::execution_context& ctx, win_tcp_service& svc)
1286 369x : svc_(svc)
1287 {
1288 (void)ctx;
1289 369x }
1290
1291 inline io_object::implementation*
1292 1180x win_tcp_acceptor_service::construct()
1293 {
1294
1/1
✓ Branch 2 → 3 taken 1180 times.
1180x auto internal = std::make_shared<win_tcp_acceptor_internal>(svc_);
1295
1296 {
1297 1180x std::lock_guard<win_mutex> lock(svc_.mutex_);
1298 1180x svc_.acceptor_list_.push_back(internal.get());
1299 1180x }
1300
1301
1/1
✓ Branch 7 → 8 taken 1180 times.
1180x auto* wrapper = new win_tcp_acceptor(std::move(internal));
1302
1303 {
1304 1180x std::lock_guard<win_mutex> lock(svc_.mutex_);
1305 1180x svc_.acceptor_wrapper_list_.push_back(wrapper);
1306 1180x }
1307
1308 1180x return wrapper;
1309 1180x }
1310
1311 inline void
1312 1180x win_tcp_acceptor_service::destroy(io_object::implementation* p)
1313 {
1314
1/2
✓ Branch 2 → 3 taken 1180 times.
✗ Branch 2 → 5 not taken.
1180x if (p)
1315 {
1316 1180x auto& wrapper = static_cast<win_tcp_acceptor&>(*p);
1317 1180x wrapper.close_internal();
1318 1180x svc_.destroy_acceptor_impl(wrapper);
1319 }
1320 1180x }
1321
1322 inline void
1323 2358x win_tcp_acceptor_service::close(io_object::handle& h)
1324 {
1325 2358x auto& wrapper = static_cast<win_tcp_acceptor&>(*h.get());
1326 2358x wrapper.get_internal()->close_socket();
1327 2358x }
1328
1329 inline std::error_code
1330 1178x win_tcp_acceptor_service::open_acceptor_socket(
1331 tcp_acceptor::implementation& impl, int family, int type, int protocol)
1332 {
1333 1178x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1334 1178x return svc_.open_acceptor_socket(
1335 2356x *wrapper.get_internal(), family, type, protocol);
1336 }
1337
1338 inline std::error_code
1339 1177x win_tcp_acceptor_service::bind_acceptor(
1340 tcp_acceptor::implementation& impl, endpoint ep)
1341 {
1342 1177x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1343 1177x return svc_.bind_acceptor(*wrapper.get_internal(), ep);
1344 }
1345
1346 inline std::error_code
1347 1174x win_tcp_acceptor_service::listen_acceptor(
1348 tcp_acceptor::implementation& impl, int backlog)
1349 {
1350 1174x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1351 1174x return svc_.listen_acceptor(*wrapper.get_internal(), backlog);
1352 }
1353
1354 inline void
1355 369x win_tcp_acceptor_service::shutdown()
1356 {
1357 369x }
1358
1359 } // namespace boost::corosio::detail
1360
1361 #endif // BOOST_COROSIO_HAS_IOCP
1362
1363 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
1364