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

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