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 3625x 100.0% 100.0% boost::corosio::detail::read_op::read_op(boost::corosio::detail::win_tcp_socket_internal&) :83 3625x 100.0% 100.0% boost::corosio::detail::write_op::write_op(boost::corosio::detail::win_tcp_socket_internal&) :90 3625x 100.0% 100.0% boost::corosio::detail::accept_op::accept_op() :97 1129x 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 744x 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 1176x 83.0% 66.7% 80.9% boost::corosio::detail::connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :251 1170x 85.7% 76.5% 82.9% boost::corosio::detail::read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :292 234957x 66.7% 66.7% 61.5% boost::corosio::detail::write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :314 233453x 66.7% 66.7% 61.5% boost::corosio::detail::win_tcp_socket_internal::win_tcp_socket_internal(boost::corosio::detail::win_tcp_service&) :335 3625x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::~win_tcp_socket_internal() :343 3625x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::native_handle() const :349 18049x 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 22x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::is_open() const :367 1914x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_socket(unsigned long long) :373 1168x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_endpoints(boost::corosio::endpoint, boost::corosio::endpoint) :379 2336x 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 1170x 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 234957x 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 233453x 96.7% 78.6% 94.7% boost::corosio::detail::win_tcp_socket_internal::cancel() :597 2107x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::close_socket() :610 10794x 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 3625x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::close_internal() :635 3625x 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 1170x 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 234957x 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 233453x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::shutdown(boost::corosio::tcp_socket::shutdown_type) :680 3x 81.2% 66.7% 76.9% boost::corosio::detail::win_tcp_socket::native_handle() const :703 12901x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::set_option(int, int, void const*, unsigned long long) :709 2032x 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 22x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::cancel() :745 2107x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::get_internal() const :751 9511x 100.0% 100.0% boost::corosio::detail::win_tcp_service::win_tcp_service(boost::capy::execution_context&) :758 431x 100.0% 100.0% 65.0% boost::corosio::detail::win_tcp_service::~win_tcp_service() :765 862x 100.0% 100.0% boost::corosio::detail::win_tcp_service::shutdown() :780 431x 55.6% 50.0% 63.6% boost::corosio::detail::win_tcp_service::construct() :803 3625x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_service::destroy(boost::corosio::io_object::implementation*) :823 3625x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::close(boost::corosio::io_object::handle&) :834 5977x 100.0% 100.0% boost::corosio::detail::win_tcp_service::destroy_impl(boost::corosio::detail::win_tcp_socket&) :841 3625x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_impl(boost::corosio::detail::win_tcp_socket_internal&) :851 3625x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_socket(boost::corosio::detail::win_tcp_socket_internal&, int, int, int) :858 1192x 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 1176x 100.0% 100.0% boost::corosio::detail::win_tcp_service::connect_ex() const :921 1170x 100.0% 100.0% boost::corosio::detail::win_tcp_service::accept_ex() const :927 1176x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_pending(boost::corosio::detail::overlapped_op*) :939 470750x 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 470756x 100.0% 100.0% boost::corosio::detail::win_tcp_service::load_extension_functions() :963 431x 91.7% 83.3% 88.9% boost::corosio::detail::win_tcp_service::destroy_acceptor_impl(boost::corosio::detail::win_tcp_acceptor&) :989 1129x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_acceptor_impl(boost::corosio::detail::win_tcp_acceptor_internal&) :999 1129x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_acceptor_socket(boost::corosio::detail::win_tcp_acceptor_internal&, int, int, int) :1006 1126x 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 1125x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::listen_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, int) :1062 1120x 80.0% 50.0% 66.7% boost::corosio::detail::win_tcp_acceptor_internal::win_tcp_acceptor_internal(boost::corosio::detail::win_tcp_service&) :1074 1129x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::~win_tcp_acceptor_internal() :1079 1129x 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 1123x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::local_endpoint() const :1097 1112x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::is_open() const :1103 9048x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::set_local_endpoint(boost::corosio::endpoint) :1109 1121x 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 1176x 60.8% 51.7% 56.7% boost::corosio::detail::win_tcp_acceptor_internal::cancel() :1208 2x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::close_socket() :1219 4510x 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 1129x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::close_internal() :1241 1129x 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 1176x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::local_endpoint() const :1262 1112x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::is_open() const :1268 9048x 100.0% 75.0% 100.0% boost::corosio::detail::win_tcp_acceptor::cancel() :1274 2x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::set_option(int, int, void const*, unsigned long long) :1280 1123x 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 5626x 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 431x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::construct() :1319 1129x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_acceptor_service::destroy(boost::corosio::io_object::implementation*) :1339 1129x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::close(boost::corosio::io_object::handle&) :1350 2255x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::open_acceptor_socket(boost::corosio::tcp_acceptor::implementation&, int, int, int) :1357 1126x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::bind_acceptor(boost::corosio::tcp_acceptor::implementation&, boost::corosio::endpoint) :1366 1125x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::listen_acceptor(boost::corosio::tcp_acceptor::implementation&, int) :1374 1120x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::shutdown() :1382 431x 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 3625x inline connect_op::connect_op(win_tcp_socket_internal& internal_) noexcept
77 : overlapped_op(&do_complete)
78 3625x , internal(internal_)
79 {
80 3625x cancel_func_ = &do_cancel_impl;
81 3625x }
82
83 3625x inline read_op::read_op(win_tcp_socket_internal& internal_) noexcept
84 : overlapped_op(&do_complete)
85 3625x , internal(internal_)
86 {
87 3625x cancel_func_ = &do_cancel_impl;
88 3625x }
89
90 3625x inline write_op::write_op(win_tcp_socket_internal& internal_) noexcept
91 : overlapped_op(&do_complete)
92 3625x , internal(internal_)
93 {
94 3625x cancel_func_ = &do_cancel_impl;
95 3625x }
96
97 1129x inline accept_op::accept_op() noexcept : overlapped_op(&do_complete)
98 {
99 1129x cancel_func_ = &do_cancel_impl;
100 1129x }
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 744x read_op::do_cancel_impl(overlapped_op* base) noexcept
117 {
118 744x auto* op = static_cast<read_op*>(base);
119 744x op->cancelled.store(true, std::memory_order_release);
120
1/2
✓ Branch 4 → 5 taken 744 times.
✗ Branch 4 → 10 not taken.
744x if (op->internal.is_open())
121 {
122
1/2
✓ Branch 5 → 6 taken 744 times.
✗ Branch 5 → 7 not taken.
1488x ::CancelIoEx(
123 744x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
124 }
125 744x }
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 1176x accept_op::do_complete(
153 void* owner,
154 scheduler_op* base,
155 std::uint32_t /*bytes*/,
156 std::uint32_t /*error*/)
157 {
158 1176x auto* op = static_cast<accept_op*>(base);
159
160
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 1176 times.
1176x 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 1176x op->stop_cb.reset();
180
181 bool success =
182
3/4
✓ Branch 13 → 14 taken 1168 times.
✓ Branch 13 → 17 taken 8 times.
✓ Branch 15 → 16 taken 1168 times.
✗ Branch 15 → 17 not taken.
1176x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
183
184
1/2
✓ Branch 18 → 19 taken 1176 times.
✗ Branch 18 → 27 not taken.
1176x if (op->ec_out)
185 {
186
2/2
✓ Branch 20 → 21 taken 7 times.
✓ Branch 20 → 23 taken 1169 times.
1176x 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 1168 times.
1169x else if (op->dwError != 0)
189 1x *op->ec_out = make_err(op->dwError);
190 else
191 1168x *op->ec_out = {};
192 }
193
194
4/6
✓ Branch 27 → 28 taken 1168 times.
✓ Branch 27 → 48 taken 8 times.
✓ Branch 28 → 29 taken 1168 times.
✗ Branch 28 → 48 not taken.
✓ Branch 29 → 30 taken 1168 times.
✗ Branch 29 → 48 not taken.
1176x if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
195 {
196 1168x ::setsockopt(
197 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
198
1/1
✓ Branch 30 → 31 taken 1168 times.
1168x reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
199
200 1168x op->peer_wrapper->get_internal()->set_socket(op->accepted_socket);
201
202 1168x sockaddr_storage local_storage{};
203 1168x int local_len = sizeof(local_storage);
204 1168x sockaddr_storage remote_storage{};
205 1168x int remote_len = sizeof(remote_storage);
206
207 1168x endpoint local_ep, remote_ep;
208
1/1
✓ Branch 35 → 36 taken 1168 times.
1168x if (::getsockname(
209 op->accepted_socket,
210
2/2
✓ Branch 36 → 37 taken 1167 times.
✓ Branch 36 → 39 taken 1 time.
1168x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
211 1167x local_ep = from_sockaddr(local_storage);
212
1/1
✓ Branch 39 → 40 taken 1168 times.
1168x if (::getpeername(
213 op->accepted_socket,
214
2/2
✓ Branch 40 → 41 taken 1167 times.
✓ Branch 40 → 43 taken 1 time.
1168x reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) == 0)
215 1167x remote_ep = from_sockaddr(remote_storage);
216
217 1168x op->peer_wrapper->get_internal()->set_endpoints(local_ep, remote_ep);
218 1168x op->accepted_socket = INVALID_SOCKET;
219
220
1/2
✓ Branch 45 → 46 taken 1168 times.
✗ Branch 45 → 47 not taken.
1168x if (op->impl_out)
221 1168x *op->impl_out = op->peer_wrapper;
222 1168x }
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 1176x op->cont_op.cont.h = op->h;
242 1176x auto saved_ex = op->ex;
243 1176x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
244
245
2/2
✓ Branch 60 → 61 taken 1176 times.
✓ Branch 61 → 62 taken 1176 times.
1176x dispatch_coro(saved_ex, op->cont_op.cont).resume();
246 1176x }
247
248 // connect_op completion handler
249
250 inline void
251 1170x connect_op::do_complete(
252 void* owner,
253 scheduler_op* base,
254 std::uint32_t /*bytes*/,
255 std::uint32_t /*error*/)
256 {
257 1170x auto* op = static_cast<connect_op*>(base);
258
259
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1170 times.
1170x 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 1168 times.
✓ Branch 6 → 10 taken 2 times.
✓ Branch 8 → 9 taken 1168 times.
✗ Branch 8 → 10 not taken.
1170x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
268
5/6
✓ Branch 11 → 12 taken 1168 times.
✓ Branch 11 → 15 taken 2 times.
✓ Branch 13 → 14 taken 1168 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 1168 times.
✓ Branch 16 → 27 taken 2 times.
1170x if (success && op->internal.is_open())
269 {
270 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
271
1/1
✓ Branch 18 → 19 taken 1168 times.
1168x ::setsockopt(
272 1168x op->internal.native_handle(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
273 nullptr, 0);
274
275 1168x endpoint local_ep;
276 1168x sockaddr_storage local_storage{};
277 1168x int local_len = sizeof(local_storage);
278
1/1
✓ Branch 21 → 22 taken 1168 times.
1168x if (::getsockname(
279 1168x op->internal.native_handle(),
280
1/2
✓ Branch 22 → 23 taken 1168 times.
✗ Branch 22 → 25 not taken.
1168x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
281 1168x local_ep = from_sockaddr(local_storage);
282 1168x op->internal.set_endpoints(local_ep, op->target_endpoint);
283 }
284
285 1170x auto prevent_premature_destruction = std::move(op->internal_ptr);
286
1/1
✓ Branch 29 → 30 taken 1170 times.
1170x op->invoke_handler();
287 1170x }
288
289 // read_op completion handler
290
291 inline void
292 234957x read_op::do_complete(
293 void* owner,
294 scheduler_op* base,
295 std::uint32_t /*bytes*/,
296 std::uint32_t /*error*/)
297 {
298 234957x auto* op = static_cast<read_op*>(base);
299
300
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 234957 times.
234957x if (!owner)
301 {
302 op->cleanup_only();
303 op->internal_ptr.reset();
304 return;
305 }
306
307 234957x auto prevent_premature_destruction = std::move(op->internal_ptr);
308
1/1
✓ Branch 8 → 9 taken 234957 times.
234957x op->invoke_handler();
309 234957x }
310
311 // write_op completion handler
312
313 inline void
314 233453x write_op::do_complete(
315 void* owner,
316 scheduler_op* base,
317 std::uint32_t /*bytes*/,
318 std::uint32_t /*error*/)
319 {
320 233453x auto* op = static_cast<write_op*>(base);
321
322
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 233453 times.
233453x if (!owner)
323 {
324 op->cleanup_only();
325 op->internal_ptr.reset();
326 return;
327 }
328
329 233453x auto prevent_premature_destruction = std::move(op->internal_ptr);
330
1/1
✓ Branch 8 → 9 taken 233453 times.
233453x op->invoke_handler();
331 233453x }
332
333 // win_tcp_socket_internal
334
335 3625x inline win_tcp_socket_internal::win_tcp_socket_internal(win_tcp_service& svc) noexcept
336 3625x : svc_(svc)
337 3625x , conn_(*this)
338 3625x , rd_(*this)
339 7250x , wr_(*this)
340 {
341 3625x }
342
343 3625x inline win_tcp_socket_internal::~win_tcp_socket_internal()
344 {
345 3625x svc_.unregister_impl(*this);
346 3625x }
347
348 inline SOCKET
349 18049x win_tcp_socket_internal::native_handle() const noexcept
350 {
351 18049x 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 22x win_tcp_socket_internal::remote_endpoint() const noexcept
362 {
363 22x return remote_endpoint_;
364 }
365
366 inline bool
367 1914x win_tcp_socket_internal::is_open() const noexcept
368 {
369 1914x return socket_ != INVALID_SOCKET;
370 }
371
372 inline void
373 1168x win_tcp_socket_internal::set_socket(SOCKET s) noexcept
374 {
375 1168x socket_ = s;
376 1168x }
377
378 inline void
379 2336x win_tcp_socket_internal::set_endpoints(endpoint local, endpoint remote) noexcept
380 {
381 2336x local_endpoint_ = local;
382 2336x remote_endpoint_ = remote;
383 2336x }
384
385 inline std::coroutine_handle<>
386 1170x 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 1170 times.
1170x conn_.internal_ptr = shared_from_this();
395
396 1170x auto& op = conn_;
397 1170x op.reset();
398 1170x op.h = h;
399 1170x op.ex = d;
400 1170x op.ec_out = ec;
401 1170x op.target_endpoint = ep;
402 1170x op.start(token);
403
404 1170x 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 1169 times.
✓ Branch 12 → 24 taken 1 time.
1170x if (local_endpoint_ == endpoint{})
409 {
410 1169x sockaddr_storage bind_storage{};
411 socklen_t bind_len;
412
2/2
✓ Branch 13 → 14 taken 4 times.
✓ Branch 13 → 15 taken 1165 times.
1169x if (family_ == AF_INET6)
413 {
414 4x sockaddr_in6 sa6{};
415 4x sa6.sin6_family = AF_INET6;
416 4x sa6.sin6_port = 0;
417 4x sa6.sin6_addr = in6addr_any;
418 4x std::memcpy(&bind_storage, &sa6, sizeof(sa6));
419 4x bind_len = sizeof(sa6);
420 }
421 else
422 {
423 1165x sockaddr_in sa4{};
424 1165x sa4.sin_family = AF_INET;
425 1165x sa4.sin_addr.s_addr = INADDR_ANY;
426 1165x sa4.sin_port = 0;
427 1165x std::memcpy(&bind_storage, &sa4, sizeof(sa4));
428 1165x bind_len = sizeof(sa4);
429 }
430
431
1/1
✓ Branch 16 → 17 taken 1169 times.
1169x if (::bind(
432 socket_, reinterpret_cast<sockaddr*>(&bind_storage),
433
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 23 taken 1169 times.
1169x bind_len) == SOCKET_ERROR)
434 {
435 svc_.on_completion(&op, ::WSAGetLastError(), 0);
436 return std::noop_coroutine();
437 }
438 }
439
440 1170x auto connect_ex = svc_.connect_ex();
441
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 30 taken 1170 times.
1170x if (!connect_ex)
442 {
443 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
444 return std::noop_coroutine();
445 }
446
447 1170x sockaddr_storage storage{};
448 1170x socklen_t addrlen = detail::to_sockaddr(ep, family_, storage);
449
450
1/1
✓ Branch 31 → 32 taken 1170 times.
1170x 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 1170 times.
✗ Branch 32 → 39 not taken.
1170x if (!result)
455 {
456
1/1
✓ Branch 33 → 34 taken 1170 times.
1170x DWORD err = ::WSAGetLastError();
457
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 39 taken 1170 times.
1170x if (err != ERROR_IO_PENDING)
458 {
459 svc_.on_completion(&op, err, 0);
460 return std::noop_coroutine();
461 }
462 }
463
464 1170x svc_.on_pending(&op);
465 1170x return std::noop_coroutine();
466 }
467
468 inline std::coroutine_handle<>
469 234957x 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 234957 times.
234957x rd_.internal_ptr = shared_from_this();
479
480 234957x auto& op = rd_;
481 234957x op.reset();
482 234957x op.is_read_ = true;
483 234957x op.h = h;
484 234957x op.ex = d;
485 234957x op.ec_out = ec;
486 234957x op.bytes_out = bytes_out;
487 234957x op.start(token);
488
489 234957x svc_.work_started();
490
491 // Prepare buffers
492 234957x capy::mutable_buffer bufs[read_op::max_buffers];
493 234957x op.wsabuf_count =
494 234957x 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 234956 times.
234957x 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 234956 times.
✓ Branch 20 → 21 taken 234956 times.
469912x for (DWORD i = 0; i < op.wsabuf_count; ++i)
505 {
506 234956x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
507 234956x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
508 }
509
510 234956x op.flags = 0;
511
512 469912x int result = ::WSARecv(
513
1/1
✓ Branch 21 → 22 taken 234956 times.
234956x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
514
515
2/2
✓ Branch 22 → 23 taken 3316 times.
✓ Branch 22 → 29 taken 231640 times.
234956x if (result == SOCKET_ERROR)
516 {
517
1/1
✓ Branch 23 → 24 taken 3316 times.
3316x DWORD err = ::WSAGetLastError();
518
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 24 → 29 taken 3313 times.
3316x if (err != WSA_IO_PENDING)
519 {
520 3x svc_.on_completion(&op, err, 0);
521 3x return std::noop_coroutine();
522 }
523 }
524
525 234953x 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 234953 times.
234953x if (op.cancelled.load(std::memory_order_acquire))
529 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
530
531 234953x return std::noop_coroutine();
532 }
533
534 inline std::coroutine_handle<>
535 233453x 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 233453 times.
233453x wr_.internal_ptr = shared_from_this();
545
546 233453x auto& op = wr_;
547 233453x op.reset();
548 233453x op.h = h;
549 233453x op.ex = d;
550 233453x op.ec_out = ec;
551 233453x op.bytes_out = bytes_out;
552 233453x op.start(token);
553
554 233453x svc_.work_started();
555
556 // Prepare buffers
557 233453x capy::mutable_buffer bufs[write_op::max_buffers];
558 233453x op.wsabuf_count =
559 233453x 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 233452 times.
233453x 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 233452 times.
✓ Branch 20 → 21 taken 233452 times.
466904x for (DWORD i = 0; i < op.wsabuf_count; ++i)
569 {
570 233452x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
571 233452x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
572 }
573
574 466904x int result = ::WSASend(
575
1/1
✓ Branch 21 → 22 taken 233452 times.
233452x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
576
577
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 29 taken 233451 times.
233452x 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 233451x 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 233451 times.
233451x if (op.cancelled.load(std::memory_order_acquire))
591 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
592
593 233451x return std::noop_coroutine();
594 }
595
596 inline void
597 2107x win_tcp_socket_internal::cancel() noexcept
598 {
599
1/2
✓ Branch 2 → 3 taken 2107 times.
✗ Branch 2 → 4 not taken.
2107x if (socket_ != INVALID_SOCKET)
600 {
601 2107x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
602 }
603
604 2107x conn_.request_cancel();
605 2107x rd_.request_cancel();
606 2107x wr_.request_cancel();
607 2107x }
608
609 inline void
610 10794x win_tcp_socket_internal::close_socket() noexcept
611 {
612
2/2
✓ Branch 2 → 3 taken 2360 times.
✓ Branch 2 → 6 taken 8434 times.
10794x if (socket_ != INVALID_SOCKET)
613 {
614 2360x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
615 2360x ::closesocket(socket_);
616 2360x socket_ = INVALID_SOCKET;
617 }
618
619 10794x family_ = AF_UNSPEC;
620
621 // Clear cached endpoints
622 10794x local_endpoint_ = endpoint{};
623 10794x remote_endpoint_ = endpoint{};
624 10794x }
625
626 // win_tcp_socket
627
628 3625x inline win_tcp_socket::win_tcp_socket(
629 3625x std::shared_ptr<win_tcp_socket_internal> internal) noexcept
630 3625x : internal_(std::move(internal))
631 {
632 3625x }
633
634 inline void
635 3625x win_tcp_socket::close_internal() noexcept
636 {
637
1/2
✓ Branch 3 → 4 taken 3625 times.
✗ Branch 3 → 7 not taken.
3625x if (internal_)
638 {
639 3625x internal_->close_socket();
640 3625x internal_.reset();
641 }
642 3625x }
643
644 inline std::coroutine_handle<>
645 1170x 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 1170 times.
1170x return internal_->connect(h, d, ep, token, ec);
653 }
654
655 inline std::coroutine_handle<>
656 234957x 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 234957 times.
234957x return internal_->read_some(h, d, buf, token, ec, bytes);
665 }
666
667 inline std::coroutine_handle<>
668 233453x 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 233453 times.
233453x 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 12901x win_tcp_socket::native_handle() const noexcept
704 {
705 12901x return static_cast<native_handle_type>(internal_->native_handle());
706 }
707
708 inline std::error_code
709 2032x win_tcp_socket::set_option(
710 int level, int optname, void const* data, std::size_t size) noexcept
711 {
712 2032x if (::setsockopt(
713 2032x internal_->native_handle(), level, optname,
714
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 2032 times.
2032x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
715 return make_err(WSAGetLastError());
716 2032x 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 22x win_tcp_socket::remote_endpoint() const noexcept
740 {
741 22x return internal_->remote_endpoint();
742 }
743
744 inline void
745 2107x win_tcp_socket::cancel() noexcept
746 {
747 2107x internal_->cancel();
748 2107x }
749
750 inline win_tcp_socket_internal*
751 9511x win_tcp_socket::get_internal() const noexcept
752 {
753 9511x return internal_.get();
754 }
755
756 // win_tcp_service
757
758 431x inline win_tcp_service::win_tcp_service(capy::execution_context& ctx)
759 862x : sched_(ctx.use_service<win_scheduler>())
760
2/2
✓ Branch 5 → 6 taken 431 times.
✓ Branch 6 → 7 taken 431 times.
431x , iocp_(sched_.native_handle())
761 {
762
1/1
✓ Branch 12 → 13 taken 431 times.
431x load_extension_functions();
763 431x }
764
765 862x 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 431 times.
431x 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 431 times.
431x for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
775 w = acceptor_wrapper_list_.pop_front())
776 delete w;
777 862x }
778
779 inline void
780 431x win_tcp_service::shutdown()
781 {
782 431x 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 431 times.
431x 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 431 times.
431x for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
796 impl = acceptor_list_.pop_front())
797 {
798 impl->close_socket();
799 }
800 431x }
801
802 inline io_object::implementation*
803 3625x win_tcp_service::construct()
804 {
805
1/1
✓ Branch 2 → 3 taken 3625 times.
3625x auto internal = std::make_shared<win_tcp_socket_internal>(*this);
806
807 {
808 3625x std::lock_guard<win_mutex> lock(mutex_);
809 3625x socket_list_.push_back(internal.get());
810 3625x }
811
812
1/1
✓ Branch 7 → 8 taken 3625 times.
3625x auto* wrapper = new win_tcp_socket(std::move(internal));
813
814 {
815 3625x std::lock_guard<win_mutex> lock(mutex_);
816 3625x socket_wrapper_list_.push_back(wrapper);
817 3625x }
818
819 3625x return wrapper;
820 3625x }
821
822 inline void
823 3625x win_tcp_service::destroy(io_object::implementation* p)
824 {
825
1/2
✓ Branch 2 → 3 taken 3625 times.
✗ Branch 2 → 5 not taken.
3625x if (p)
826 {
827 3625x auto& wrapper = static_cast<win_tcp_socket&>(*p);
828 3625x wrapper.close_internal();
829 3625x destroy_impl(wrapper);
830 }
831 3625x }
832
833 inline void
834 5977x win_tcp_service::close(io_object::handle& h)
835 {
836 5977x auto& wrapper = static_cast<win_tcp_socket&>(*h.get());
837 5977x wrapper.get_internal()->close_socket();
838 5977x }
839
840 inline void
841 3625x win_tcp_service::destroy_impl(win_tcp_socket& impl)
842 {
843 {
844 3625x std::lock_guard<win_mutex> lock(mutex_);
845 3625x socket_wrapper_list_.remove(&impl);
846 3625x }
847
1/2
✓ Branch 5 → 6 taken 3625 times.
✗ Branch 5 → 7 not taken.
3625x delete &impl;
848 3625x }
849
850 inline void
851 3625x win_tcp_service::unregister_impl(win_tcp_socket_internal& impl)
852 {
853 3625x std::lock_guard<win_mutex> lock(mutex_);
854 3625x socket_list_.remove(&impl);
855 3625x }
856
857 inline std::error_code
858 1192x win_tcp_service::open_socket(
859 win_tcp_socket_internal& impl, int family, int type, int protocol)
860 {
861 1192x impl.close_socket();
862
863 SOCKET sock =
864 1192x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
865
866
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1192 times.
1192x if (sock == INVALID_SOCKET)
867 return make_err(::WSAGetLastError());
868
869
2/2
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 10 taken 1186 times.
1192x if (family == AF_INET6)
870 {
871 6x DWORD one = 1;
872
1/1
✓ Branch 8 → 9 taken 6 times.
6x ::setsockopt(
873 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
874 sizeof(one));
875 }
876
877 2384x HANDLE result = ::CreateIoCompletionPort(
878 1192x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
879
880
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1192 times.
1192x if (result == nullptr)
881 {
882 DWORD dwError = ::GetLastError();
883 ::closesocket(sock);
884 return make_err(dwError);
885 }
886
887 1192x impl.socket_ = sock;
888 1192x impl.family_ = family;
889 1192x 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 1176x win_tcp_service::native_handle() const noexcept
916 {
917 1176x return iocp_;
918 }
919
920 inline LPFN_CONNECTEX
921 1170x win_tcp_service::connect_ex() const noexcept
922 {
923 1170x return connect_ex_;
924 }
925
926 inline LPFN_ACCEPTEX
927 1176x win_tcp_service::accept_ex() const noexcept
928 {
929 1176x 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 470750x win_tcp_service::on_pending(overlapped_op* op) noexcept
940 {
941 470750x sched_.on_pending(op);
942 470750x }
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 470756x win_tcp_service::work_started() noexcept
952 {
953 470756x sched_.work_started();
954 470756x }
955
956 inline void
957 win_tcp_service::work_finished() noexcept
958 {
959 sched_.work_finished();
960 }
961
962 inline void
963 431x win_tcp_service::load_extension_functions()
964 {
965
1/1
✓ Branch 2 → 3 taken 431 times.
431x 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 431 times.
431x if (sock == INVALID_SOCKET)
969 return;
970
971 431x DWORD bytes = 0;
972
973 431x GUID connect_ex_guid = WSAID_CONNECTEX;
974 431x ::WSAIoctl(
975 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
976
1/1
✓ Branch 5 → 6 taken 431 times.
431x sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
977 nullptr, nullptr);
978
979 431x GUID accept_ex_guid = WSAID_ACCEPTEX;
980 431x ::WSAIoctl(
981 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
982
1/1
✓ Branch 6 → 7 taken 431 times.
431x sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
983 nullptr, nullptr);
984
985
1/1
✓ Branch 7 → 8 taken 431 times.
431x ::closesocket(sock);
986 }
987
988 inline void
989 1129x win_tcp_service::destroy_acceptor_impl(win_tcp_acceptor& impl)
990 {
991 {
992 1129x std::lock_guard<win_mutex> lock(mutex_);
993 1129x acceptor_wrapper_list_.remove(&impl);
994 1129x }
995
1/2
✓ Branch 5 → 6 taken 1129 times.
✗ Branch 5 → 7 not taken.
1129x delete &impl;
996 1129x }
997
998 inline void
999 1129x win_tcp_service::unregister_acceptor_impl(win_tcp_acceptor_internal& impl)
1000 {
1001 1129x std::lock_guard<win_mutex> lock(mutex_);
1002 1129x acceptor_list_.remove(&impl);
1003 1129x }
1004
1005 inline std::error_code
1006 1126x win_tcp_service::open_acceptor_socket(
1007 win_tcp_acceptor_internal& impl, int family, int type, int protocol)
1008 {
1009 1126x impl.close_socket();
1010
1011 SOCKET sock =
1012 1126x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1013
1014
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1126 times.
1126x if (sock == INVALID_SOCKET)
1015 return make_err(::WSAGetLastError());
1016
1017
2/2
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 10 taken 1118 times.
1126x if (family == AF_INET6)
1018 {
1019 8x DWORD val = 0; // dual-stack default
1020
1/1
✓ Branch 8 → 9 taken 8 times.
8x ::setsockopt(
1021 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&val),
1022 sizeof(val));
1023 }
1024
1025 2252x HANDLE result = ::CreateIoCompletionPort(
1026 1126x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1027
1028
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1126 times.
1126x if (result == nullptr)
1029 {
1030 DWORD dwError = ::GetLastError();
1031 ::closesocket(sock);
1032 return make_err(dwError);
1033 }
1034
1035 1126x impl.socket_ = sock;
1036 1126x return {};
1037 }
1038
1039 inline std::error_code
1040 1125x win_tcp_service::bind_acceptor(win_tcp_acceptor_internal& impl, endpoint ep)
1041 {
1042 1125x SOCKET sock = impl.socket_;
1043
1044 1125x sockaddr_storage storage{};
1045 1125x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1046
1/1
✓ Branch 3 → 4 taken 1125 times.
1125x if (::bind(
1047 sock, reinterpret_cast<sockaddr*>(&storage),
1048
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 7 taken 1121 times.
1125x 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 1121x sockaddr_storage local_storage{};
1053 1121x int local_len = sizeof(local_storage);
1054
1/1
✓ Branch 7 → 8 taken 1121 times.
1121x if (::getsockname(
1055
1/2
✓ Branch 8 → 9 taken 1121 times.
✗ Branch 8 → 11 not taken.
1121x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1056 1121x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1057
1058 1121x return {};
1059 }
1060
1061 inline std::error_code
1062 1120x win_tcp_service::listen_acceptor(win_tcp_acceptor_internal& impl, int backlog)
1063 {
1064 1120x SOCKET sock = impl.socket_;
1065
1066
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1120 times.
1120x if (::listen(sock, backlog) == SOCKET_ERROR)
1067 return make_err(::WSAGetLastError());
1068
1069 1120x return {};
1070 }
1071
1072 // win_tcp_acceptor_internal
1073
1074 1129x inline win_tcp_acceptor_internal::win_tcp_acceptor_internal(win_tcp_service& svc) noexcept
1075 1129x : svc_(svc)
1076 {
1077 1129x }
1078
1079 1129x inline win_tcp_acceptor_internal::~win_tcp_acceptor_internal()
1080 {
1081 1129x svc_.unregister_acceptor_impl(*this);
1082 1129x }
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 1123x win_tcp_acceptor_internal::native_handle() const noexcept
1092 {
1093 1123x return socket_;
1094 }
1095
1096 inline endpoint
1097 1112x win_tcp_acceptor_internal::local_endpoint() const noexcept
1098 {
1099 1112x return local_endpoint_;
1100 }
1101
1102 inline bool
1103 9048x win_tcp_acceptor_internal::is_open() const noexcept
1104 {
1105 9048x return socket_ != INVALID_SOCKET;
1106 }
1107
1108 inline void
1109 1121x win_tcp_acceptor_internal::set_local_endpoint(endpoint ep) noexcept
1110 {
1111 1121x local_endpoint_ = ep;
1112 1121x }
1113
1114 inline std::coroutine_handle<>
1115 1176x 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 1176 times.
1176x acc_.acceptor_ptr = shared_from_this();
1124
1125 1176x auto& op = acc_;
1126 1176x op.reset();
1127 1176x op.h = h;
1128 1176x op.ex = d;
1129 1176x op.ec_out = ec;
1130 1176x op.impl_out = impl_out;
1131 1176x op.start(token);
1132
1133 1176x svc_.work_started();
1134
1135 // Create wrapper for the peer socket (service owns it)
1136
1/1
✓ Branch 10 → 11 taken 1176 times.
1176x 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 5 times.
✓ Branch 12 → 14 taken 1171 times.
1176x 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 1176 times.
1176x 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 1176 times.
1176x 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 1176 times.
1176x HANDLE result = ::CreateIoCompletionPort(
1153 1176x reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
1154
1155
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 33 taken 1176 times.
1176x 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 1176x op.accepted_socket = accepted;
1166 1176x op.peer_wrapper = &peer_wrapper;
1167 1176x op.listen_socket = socket_;
1168
1169 1176x auto accept_ex = svc_.accept_ex();
1170
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 41 taken 1176 times.
1176x 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 5 times.
✓ Branch 41 → 43 taken 1171 times.
1176x DWORD addr_size = static_cast<DWORD>(
1182 (af == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) + 16);
1183 1176x DWORD bytes_received = 0;
1184
1185 2352x BOOL ok = accept_ex(
1186
1/1
✓ Branch 44 → 45 taken 1176 times.
1176x socket_, accepted, op.addr_buf, 0, addr_size, addr_size,
1187 &bytes_received, &op);
1188
1189
1/2
✓ Branch 45 → 46 taken 1176 times.
✗ Branch 45 → 54 not taken.
1176x if (!ok)
1190 {
1191
1/1
✓ Branch 46 → 47 taken 1176 times.
1176x DWORD err = ::WSAGetLastError();
1192
1/2
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 54 taken 1176 times.
1176x 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 1176x svc_.on_pending(&op);
1204 1176x return std::noop_coroutine();
1205 }
1206
1207 inline void
1208 2x win_tcp_acceptor_internal::cancel() noexcept
1209 {
1210
1/2
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 4 not taken.
2x if (socket_ != INVALID_SOCKET)
1211 {
1212 2x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1213 }
1214
1215 2x acc_.request_cancel();
1216 2x }
1217
1218 inline void
1219 4510x win_tcp_acceptor_internal::close_socket() noexcept
1220 {
1221
2/2
✓ Branch 2 → 3 taken 1126 times.
✓ Branch 2 → 6 taken 3384 times.
4510x if (socket_ != INVALID_SOCKET)
1222 {
1223 1126x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1224 1126x ::closesocket(socket_);
1225 1126x socket_ = INVALID_SOCKET;
1226 }
1227
1228 // Clear cached endpoint
1229 4510x local_endpoint_ = endpoint{};
1230 4510x }
1231
1232 // win_tcp_acceptor
1233
1234 1129x inline win_tcp_acceptor::win_tcp_acceptor(
1235 1129x std::shared_ptr<win_tcp_acceptor_internal> internal) noexcept
1236 1129x : internal_(std::move(internal))
1237 {
1238 1129x }
1239
1240 inline void
1241 1129x win_tcp_acceptor::close_internal() noexcept
1242 {
1243
1/2
✓ Branch 3 → 4 taken 1129 times.
✗ Branch 3 → 7 not taken.
1129x if (internal_)
1244 {
1245 1129x internal_->close_socket();
1246 1129x internal_.reset();
1247 }
1248 1129x }
1249
1250 inline std::coroutine_handle<>
1251 1176x 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 1176 times.
1176x return internal_->accept(h, d, token, ec, impl_out);
1259 }
1260
1261 inline endpoint
1262 1112x win_tcp_acceptor::local_endpoint() const noexcept
1263 {
1264 1112x return internal_->local_endpoint();
1265 }
1266
1267 inline bool
1268 9048x win_tcp_acceptor::is_open() const noexcept
1269 {
1270
3/4
✓ Branch 3 → 4 taken 9048 times.
✗ Branch 3 → 8 not taken.
✓ Branch 6 → 7 taken 6803 times.
✓ Branch 6 → 8 taken 2245 times.
9048x return internal_ && internal_->is_open();
1271 }
1272
1273 inline void
1274 2x win_tcp_acceptor::cancel() noexcept
1275 {
1276 2x internal_->cancel();
1277 2x }
1278
1279 inline std::error_code
1280 1123x win_tcp_acceptor::set_option(
1281 int level, int optname, void const* data, std::size_t size) noexcept
1282 {
1283 1123x if (::setsockopt(
1284 1123x internal_->native_handle(), level, optname,
1285
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1123 times.
1123x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
1286 return make_err(WSAGetLastError());
1287 1123x 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 5626x win_tcp_acceptor::get_internal() const noexcept
1305 {
1306 5626x return internal_.get();
1307 }
1308
1309 // win_tcp_acceptor_service
1310
1311 431x inline win_tcp_acceptor_service::win_tcp_acceptor_service(
1312 431x capy::execution_context& ctx, win_tcp_service& svc)
1313 431x : svc_(svc)
1314 {
1315 (void)ctx;
1316 431x }
1317
1318 inline io_object::implementation*
1319 1129x win_tcp_acceptor_service::construct()
1320 {
1321
1/1
✓ Branch 2 → 3 taken 1129 times.
1129x auto internal = std::make_shared<win_tcp_acceptor_internal>(svc_);
1322
1323 {
1324 1129x std::lock_guard<win_mutex> lock(svc_.mutex_);
1325 1129x svc_.acceptor_list_.push_back(internal.get());
1326 1129x }
1327
1328
1/1
✓ Branch 7 → 8 taken 1129 times.
1129x auto* wrapper = new win_tcp_acceptor(std::move(internal));
1329
1330 {
1331 1129x std::lock_guard<win_mutex> lock(svc_.mutex_);
1332 1129x svc_.acceptor_wrapper_list_.push_back(wrapper);
1333 1129x }
1334
1335 1129x return wrapper;
1336 1129x }
1337
1338 inline void
1339 1129x win_tcp_acceptor_service::destroy(io_object::implementation* p)
1340 {
1341
1/2
✓ Branch 2 → 3 taken 1129 times.
✗ Branch 2 → 5 not taken.
1129x if (p)
1342 {
1343 1129x auto& wrapper = static_cast<win_tcp_acceptor&>(*p);
1344 1129x wrapper.close_internal();
1345 1129x svc_.destroy_acceptor_impl(wrapper);
1346 }
1347 1129x }
1348
1349 inline void
1350 2255x win_tcp_acceptor_service::close(io_object::handle& h)
1351 {
1352 2255x auto& wrapper = static_cast<win_tcp_acceptor&>(*h.get());
1353 2255x wrapper.get_internal()->close_socket();
1354 2255x }
1355
1356 inline std::error_code
1357 1126x win_tcp_acceptor_service::open_acceptor_socket(
1358 tcp_acceptor::implementation& impl, int family, int type, int protocol)
1359 {
1360 1126x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1361 1126x return svc_.open_acceptor_socket(
1362 2252x *wrapper.get_internal(), family, type, protocol);
1363 }
1364
1365 inline std::error_code
1366 1125x win_tcp_acceptor_service::bind_acceptor(
1367 tcp_acceptor::implementation& impl, endpoint ep)
1368 {
1369 1125x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1370 1125x return svc_.bind_acceptor(*wrapper.get_internal(), ep);
1371 }
1372
1373 inline std::error_code
1374 1120x win_tcp_acceptor_service::listen_acceptor(
1375 tcp_acceptor::implementation& impl, int backlog)
1376 {
1377 1120x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1378 1120x return svc_.listen_acceptor(*wrapper.get_internal(), backlog);
1379 }
1380
1381 inline void
1382 431x win_tcp_acceptor_service::shutdown()
1383 {
1384 431x }
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