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

86.9% Lines (641/738) 97.9% List of functions (93/95) 67.6% Branches (194/287)
win_tcp_acceptor_service.hpp
f(x) Functions (95)
Function Calls Lines Branches Blocks
boost::corosio::detail::connect_op::connect_op(boost::corosio::detail::win_tcp_socket_internal&) :76 4280x 100.0% 100.0% boost::corosio::detail::read_op::read_op(boost::corosio::detail::win_tcp_socket_internal&) :83 4280x 100.0% 100.0% boost::corosio::detail::write_op::write_op(boost::corosio::detail::win_tcp_socket_internal&) :90 4280x 100.0% 100.0% boost::corosio::detail::wait_op::wait_op(boost::corosio::detail::win_tcp_socket_internal&) :97 4280x 100.0% 100.0% boost::corosio::detail::accept_op::accept_op() :104 1349x 100.0% 100.0% boost::corosio::detail::acceptor_wait_op::acceptor_wait_op() :109 1349x 100.0% 100.0% boost::corosio::detail::connect_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :118 1x 100.0% 50.0% 87.5% boost::corosio::detail::read_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :129 912x 100.0% 50.0% 88.9% boost::corosio::detail::write_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :141 4x 100.0% 50.0% 88.9% boost::corosio::detail::wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :153 1x 100.0% 50.0% 90.9% boost::corosio::detail::accept_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :173 12x 100.0% 75.0% 83.3% boost::corosio::detail::acceptor_wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :183 0 0.0% 0.0% 0.0% boost::corosio::detail::accept_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :201 1386x 83.0% 66.7% 80.9% boost::corosio::detail::acceptor_wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :300 2x 66.7% 66.7% 61.5% boost::corosio::detail::connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :322 1382x 85.7% 76.5% 82.9% boost::corosio::detail::read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :363 294821x 66.7% 66.7% 61.5% boost::corosio::detail::write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :385 293141x 66.7% 66.7% 61.5% boost::corosio::detail::wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :407 5x 66.7% 66.7% 61.5% boost::corosio::detail::win_tcp_socket_internal::win_tcp_socket_internal(boost::corosio::detail::win_tcp_service&) :428 4280x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::~win_tcp_socket_internal() :437 4280x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::native_handle() const :443 21077x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::local_endpoint() const :449 26x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::remote_endpoint() const :455 25x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::is_open() const :461 2291x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_socket(unsigned long long) :467 1372x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_endpoints(boost::corosio::endpoint, boost::corosio::endpoint) :473 2745x 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*) :480 1382x 86.0% 70.6% 65.9% boost::corosio::detail::win_tcp_socket_internal::read_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :563 294821x 100.0% 100.0% 97.2% 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*) :629 293141x 100.0% 92.9% 97.2% boost::corosio::detail::win_tcp_socket_internal::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :691 5x 86.2% 72.7% 71.4% boost::corosio::detail::win_tcp_socket_internal::cancel() :756 2144x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::close_socket() :775 12736x 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>) :801 4280x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::close_internal() :808 4280x 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*) :818 1382x 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*) :829 294821x 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*) :841 293141x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :853 5x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::shutdown(boost::corosio::shutdown_type) :864 3x 81.2% 66.7% 76.9% boost::corosio::detail::win_tcp_socket::native_handle() const :887 14957x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::set_option(int, int, void const*, unsigned long long) :893 2413x 83.3% 50.0% 75.0% boost::corosio::detail::win_tcp_socket::get_option(int, int, void*, unsigned long long*) const :904 40x 87.5% 50.0% 77.8% boost::corosio::detail::win_tcp_socket::local_endpoint() const :917 26x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::remote_endpoint() const :923 25x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::cancel() :929 2144x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::get_internal() const :935 11207x 100.0% 100.0% boost::corosio::detail::win_tcp_service::win_tcp_service(boost::capy::execution_context&) :942 598x 100.0% 100.0% 65.0% boost::corosio::detail::win_tcp_service::~win_tcp_service() :949 1196x 100.0% 100.0% boost::corosio::detail::win_tcp_service::shutdown() :964 598x 55.6% 50.0% 63.6% boost::corosio::detail::win_tcp_service::construct() :987 4280x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_service::destroy(boost::corosio::io_object::implementation*) :1007 4280x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::close(boost::corosio::io_object::handle&) :1018 7047x 100.0% 100.0% boost::corosio::detail::win_tcp_service::destroy_impl(boost::corosio::detail::win_tcp_socket&) :1025 4280x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_impl(boost::corosio::detail::win_tcp_socket_internal&) :1035 4280x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_socket(boost::corosio::detail::win_tcp_socket_internal&, int, int, int) :1042 1409x 76.5% 71.4% 68.8% boost::corosio::detail::win_tcp_service::bind_socket(boost::corosio::detail::win_tcp_socket_internal&, boost::corosio::endpoint) :1077 7x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::native_handle() const :1099 1386x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_pending(boost::corosio::detail::overlapped_op*) :1111 590720x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1117 15x 100.0% 100.0% boost::corosio::detail::win_tcp_service::work_started() :1123 590737x 100.0% 100.0% boost::corosio::detail::win_tcp_service::load_extension_functions() :1135 598x 91.7% 83.3% 88.9% boost::corosio::detail::win_tcp_service::destroy_acceptor_impl(boost::corosio::detail::win_tcp_acceptor&) :1161 1349x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_acceptor_impl(boost::corosio::detail::win_tcp_acceptor_internal&) :1171 1349x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_acceptor_socket(boost::corosio::detail::win_tcp_acceptor_internal&, int, int, int) :1178 1343x 75.0% 71.4% 68.8% boost::corosio::detail::win_tcp_service::bind_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, boost::corosio::endpoint) :1212 1342x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::listen_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, int) :1234 1331x 80.0% 50.0% 66.7% boost::corosio::detail::win_tcp_acceptor_internal::win_tcp_acceptor_internal(boost::corosio::detail::win_tcp_service&) :1246 1349x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::~win_tcp_acceptor_internal() :1251 1349x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::socket_service() :1257 14x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::native_handle() const :1263 1340x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::local_endpoint() const :1269 1328x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::is_open() const :1275 10766x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::set_local_endpoint(boost::corosio::endpoint) :1281 1338x 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**) :1287 1386x 62.3% 56.2% 57.4% boost::corosio::detail::win_tcp_acceptor_internal::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :1388 2x 87.5% 66.7% 78.9% boost::corosio::detail::win_tcp_acceptor_internal::cancel() :1421 3x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::close_socket() :1434 5384x 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>) :1453 1349x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::close_internal() :1460 1349x 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**) :1470 1386x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :1481 2x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::local_endpoint() const :1492 1328x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::is_open() const :1498 10766x 100.0% 75.0% 100.0% boost::corosio::detail::win_tcp_acceptor::cancel() :1504 3x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::set_option(int, int, void const*, unsigned long long) :1510 1340x 83.3% 50.0% 75.0% boost::corosio::detail::win_tcp_acceptor::get_option(int, int, void*, unsigned long long*) const :1521 0 0.0% 0.0% 0.0% boost::corosio::detail::win_tcp_acceptor::get_internal() const :1534 6708x 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&) :1541 598x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::construct() :1549 1349x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_acceptor_service::destroy(boost::corosio::io_object::implementation*) :1569 1349x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::close(boost::corosio::io_object::handle&) :1580 2692x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::open_acceptor_socket(boost::corosio::tcp_acceptor::implementation&, int, int, int) :1587 1343x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::bind_acceptor(boost::corosio::tcp_acceptor::implementation&, boost::corosio::endpoint) :1596 1342x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::listen_acceptor(boost::corosio::tcp_acceptor::implementation&, int) :1604 1331x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::shutdown() :1612 598x 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 4280x inline connect_op::connect_op(win_tcp_socket_internal& internal_) noexcept
77 : overlapped_op(&do_complete)
78 4280x , internal(internal_)
79 {
80 4280x cancel_func_ = &do_cancel_impl;
81 4280x }
82
83 4280x inline read_op::read_op(win_tcp_socket_internal& internal_) noexcept
84 : overlapped_op(&do_complete)
85 4280x , internal(internal_)
86 {
87 4280x cancel_func_ = &do_cancel_impl;
88 4280x }
89
90 4280x inline write_op::write_op(win_tcp_socket_internal& internal_) noexcept
91 : overlapped_op(&do_complete)
92 4280x , internal(internal_)
93 {
94 4280x cancel_func_ = &do_cancel_impl;
95 4280x }
96
97 4280x inline wait_op::wait_op(win_tcp_socket_internal& internal_) noexcept
98 : overlapped_op(&do_complete)
99 4280x , internal(internal_)
100 {
101 4280x cancel_func_ = &do_cancel_impl;
102 4280x }
103
104 1349x inline accept_op::accept_op() noexcept : overlapped_op(&do_complete)
105 {
106 1349x cancel_func_ = &do_cancel_impl;
107 1349x }
108
109 1349x inline acceptor_wait_op::acceptor_wait_op() noexcept
110 1349x : overlapped_op(&do_complete)
111 {
112 1349x cancel_func_ = &do_cancel_impl;
113 1349x }
114
115 // Cancellation functions
116
117 inline void
118 1x connect_op::do_cancel_impl(overlapped_op* base) noexcept
119 {
120 1x auto* op = static_cast<connect_op*>(base);
121
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 9 not taken.
1x if (op->internal.is_open())
122 {
123
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
2x ::CancelIoEx(
124 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
125 }
126 1x }
127
128 inline void
129 912x read_op::do_cancel_impl(overlapped_op* base) noexcept
130 {
131 912x auto* op = static_cast<read_op*>(base);
132 912x op->cancelled.store(true, std::memory_order_release);
133
1/2
✓ Branch 4 → 5 taken 912 times.
✗ Branch 4 → 10 not taken.
912x if (op->internal.is_open())
134 {
135
1/2
✓ Branch 5 → 6 taken 912 times.
✗ Branch 5 → 7 not taken.
1824x ::CancelIoEx(
136 912x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
137 }
138 912x }
139
140 inline void
141 4x write_op::do_cancel_impl(overlapped_op* base) noexcept
142 {
143 4x auto* op = static_cast<write_op*>(base);
144 4x op->cancelled.store(true, std::memory_order_release);
145
1/2
✓ Branch 4 → 5 taken 4 times.
✗ Branch 4 → 10 not taken.
4x if (op->internal.is_open())
146 {
147
1/2
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 7 not taken.
8x ::CancelIoEx(
148 4x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
149 }
150 4x }
151
152 inline void
153 1x wait_op::do_cancel_impl(overlapped_op* base) noexcept
154 {
155 1x auto* op = static_cast<wait_op*>(base);
156 1x op->cancelled.store(true, std::memory_order_release);
157 // Best-effort cancel of any pending zero-byte WSARecv issued for
158 // wait_type::read. ERROR_NOT_FOUND when nothing overlapped is
159 // pending is harmless.
160
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
161 {
162
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
163 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
164 }
165 // wait_type::error parks the op in the auxiliary select reactor;
166 // wake it so the reactor can post a cancelled completion. No-op
167 // if the reactor was never constructed (e.g. zero-byte WSARecv
168 // path was the only thing this socket ever did).
169 1x op->internal.svc_.scheduler().cancel_wait_if_constructed(op);
170 1x }
171
172 inline void
173 12x accept_op::do_cancel_impl(overlapped_op* base) noexcept
174 {
175 12x auto* op = static_cast<accept_op*>(base);
176
2/2
✓ Branch 2 → 3 taken 11 times.
✓ Branch 2 → 7 taken 1 time.
12x if (op->listen_socket != INVALID_SOCKET)
177 {
178
1/2
✓ Branch 3 → 4 taken 11 times.
✗ Branch 3 → 5 not taken.
11x ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
179 }
180 12x }
181
182 inline void
183 acceptor_wait_op::do_cancel_impl(overlapped_op* base) noexcept
184 {
185 auto* op = static_cast<acceptor_wait_op*>(base);
186 op->cancelled.store(true, std::memory_order_release);
187 if (op->listen_socket != INVALID_SOCKET)
188 {
189 ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
190 }
191 if (op->acceptor_ptr)
192 {
193 op->acceptor_ptr->socket_service().scheduler()
194 .cancel_wait_if_constructed(op);
195 }
196 }
197
198 // accept_op completion handler
199
200 inline void
201 1386x accept_op::do_complete(
202 void* owner,
203 scheduler_op* base,
204 std::uint32_t /*bytes*/,
205 std::uint32_t /*error*/)
206 {
207 1386x auto* op = static_cast<accept_op*>(base);
208
209
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 1386 times.
1386x if (!owner)
210 {
211 if (op->accepted_socket != INVALID_SOCKET)
212 {
213 ::closesocket(op->accepted_socket);
214 op->accepted_socket = INVALID_SOCKET;
215 }
216
217 if (op->peer_wrapper)
218 {
219 op->peer_wrapper->close_internal();
220 op->peer_wrapper = nullptr;
221 }
222
223 op->cleanup_only();
224 op->acceptor_ptr.reset();
225 return;
226 }
227
228 1386x op->stop_cb.reset();
229
230 bool success =
231
3/4
✓ Branch 13 → 14 taken 1372 times.
✓ Branch 13 → 17 taken 14 times.
✓ Branch 15 → 16 taken 1372 times.
✗ Branch 15 → 17 not taken.
1386x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
232
233
1/2
✓ Branch 18 → 19 taken 1386 times.
✗ Branch 18 → 27 not taken.
1386x if (op->ec_out)
234 {
235
2/2
✓ Branch 20 → 21 taken 13 times.
✓ Branch 20 → 23 taken 1373 times.
1386x if (op->cancelled.load(std::memory_order_acquire))
236 13x *op->ec_out = capy::error::canceled;
237
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 1372 times.
1373x else if (op->dwError != 0)
238 1x *op->ec_out = make_err(op->dwError);
239 else
240 1372x *op->ec_out = {};
241 }
242
243
4/6
✓ Branch 27 → 28 taken 1372 times.
✓ Branch 27 → 48 taken 14 times.
✓ Branch 28 → 29 taken 1372 times.
✗ Branch 28 → 48 not taken.
✓ Branch 29 → 30 taken 1372 times.
✗ Branch 29 → 48 not taken.
1386x if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
244 {
245 1372x ::setsockopt(
246 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
247
1/1
✓ Branch 30 → 31 taken 1372 times.
1372x reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
248
249 1372x op->peer_wrapper->get_internal()->set_socket(op->accepted_socket);
250
251 1372x sockaddr_storage local_storage{};
252 1372x int local_len = sizeof(local_storage);
253 1372x sockaddr_storage remote_storage{};
254 1372x int remote_len = sizeof(remote_storage);
255
256 1372x endpoint local_ep, remote_ep;
257
1/1
✓ Branch 35 → 36 taken 1372 times.
1372x if (::getsockname(
258 op->accepted_socket,
259
2/2
✓ Branch 36 → 37 taken 1371 times.
✓ Branch 36 → 39 taken 1 time.
1372x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
260 1371x local_ep = from_sockaddr(local_storage);
261
1/1
✓ Branch 39 → 40 taken 1372 times.
1372x if (::getpeername(
262 op->accepted_socket,
263
2/2
✓ Branch 40 → 41 taken 1371 times.
✓ Branch 40 → 43 taken 1 time.
1372x reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) == 0)
264 1371x remote_ep = from_sockaddr(remote_storage);
265
266 1372x op->peer_wrapper->get_internal()->set_endpoints(local_ep, remote_ep);
267 1372x op->accepted_socket = INVALID_SOCKET;
268
269
1/2
✓ Branch 45 → 46 taken 1372 times.
✗ Branch 45 → 47 not taken.
1372x if (op->impl_out)
270 1372x *op->impl_out = op->peer_wrapper;
271 1372x }
272 else
273 {
274
1/2
✓ Branch 48 → 49 taken 14 times.
✗ Branch 48 → 51 not taken.
14x if (op->accepted_socket != INVALID_SOCKET)
275 {
276
1/1
✓ Branch 49 → 50 taken 14 times.
14x ::closesocket(op->accepted_socket);
277 14x op->accepted_socket = INVALID_SOCKET;
278 }
279
280
1/2
✓ Branch 51 → 52 taken 14 times.
✗ Branch 51 → 56 not taken.
14x if (op->peer_wrapper)
281 {
282
1/1
✓ Branch 54 → 55 taken 14 times.
14x op->acceptor_ptr->socket_service().destroy(op->peer_wrapper);
283 14x op->peer_wrapper = nullptr;
284 }
285
286
1/2
✓ Branch 56 → 57 taken 14 times.
✗ Branch 56 → 58 not taken.
14x if (op->impl_out)
287 14x *op->impl_out = nullptr;
288 }
289
290 1386x op->cont_op.cont.h = op->h;
291 1386x auto saved_ex = op->ex;
292 1386x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
293
294
2/2
✓ Branch 60 → 61 taken 1386 times.
✓ Branch 61 → 62 taken 1386 times.
1386x dispatch_coro(saved_ex, op->cont_op.cont).resume();
295 1386x }
296
297 // acceptor_wait_op completion handler
298
299 inline void
300 2x acceptor_wait_op::do_complete(
301 void* owner,
302 scheduler_op* base,
303 std::uint32_t /*bytes*/,
304 std::uint32_t /*error*/)
305 {
306 2x auto* op = static_cast<acceptor_wait_op*>(base);
307
308
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 2 times.
2x if (!owner)
309 {
310 op->cleanup_only();
311 op->acceptor_ptr.reset();
312 return;
313 }
314
315 2x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
316
1/1
✓ Branch 8 → 9 taken 2 times.
2x op->invoke_handler();
317 2x }
318
319 // connect_op completion handler
320
321 inline void
322 1382x connect_op::do_complete(
323 void* owner,
324 scheduler_op* base,
325 std::uint32_t /*bytes*/,
326 std::uint32_t /*error*/)
327 {
328 1382x auto* op = static_cast<connect_op*>(base);
329
330
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1382 times.
1382x if (!owner)
331 {
332 op->cleanup_only();
333 op->internal_ptr.reset();
334 return;
335 }
336
337 bool success =
338
3/4
✓ Branch 6 → 7 taken 1373 times.
✓ Branch 6 → 10 taken 9 times.
✓ Branch 8 → 9 taken 1373 times.
✗ Branch 8 → 10 not taken.
1382x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
339
5/6
✓ Branch 11 → 12 taken 1373 times.
✓ Branch 11 → 15 taken 9 times.
✓ Branch 13 → 14 taken 1373 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 1373 times.
✓ Branch 16 → 27 taken 9 times.
1382x if (success && op->internal.is_open())
340 {
341 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
342
1/1
✓ Branch 18 → 19 taken 1373 times.
1373x ::setsockopt(
343 1373x op->internal.native_handle(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
344 nullptr, 0);
345
346 1373x endpoint local_ep;
347 1373x sockaddr_storage local_storage{};
348 1373x int local_len = sizeof(local_storage);
349
1/1
✓ Branch 21 → 22 taken 1373 times.
1373x if (::getsockname(
350 1373x op->internal.native_handle(),
351
1/2
✓ Branch 22 → 23 taken 1373 times.
✗ Branch 22 → 25 not taken.
1373x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
352 1373x local_ep = from_sockaddr(local_storage);
353 1373x op->internal.set_endpoints(local_ep, op->target_endpoint);
354 }
355
356 1382x auto prevent_premature_destruction = std::move(op->internal_ptr);
357
1/1
✓ Branch 29 → 30 taken 1382 times.
1382x op->invoke_handler();
358 1382x }
359
360 // read_op completion handler
361
362 inline void
363 294821x read_op::do_complete(
364 void* owner,
365 scheduler_op* base,
366 std::uint32_t /*bytes*/,
367 std::uint32_t /*error*/)
368 {
369 294821x auto* op = static_cast<read_op*>(base);
370
371
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 294821 times.
294821x if (!owner)
372 {
373 op->cleanup_only();
374 op->internal_ptr.reset();
375 return;
376 }
377
378 294821x auto prevent_premature_destruction = std::move(op->internal_ptr);
379
1/1
✓ Branch 8 → 9 taken 294821 times.
294821x op->invoke_handler();
380 294821x }
381
382 // write_op completion handler
383
384 inline void
385 293141x write_op::do_complete(
386 void* owner,
387 scheduler_op* base,
388 std::uint32_t /*bytes*/,
389 std::uint32_t /*error*/)
390 {
391 293141x auto* op = static_cast<write_op*>(base);
392
393
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 293141 times.
293141x if (!owner)
394 {
395 op->cleanup_only();
396 op->internal_ptr.reset();
397 return;
398 }
399
400 293141x auto prevent_premature_destruction = std::move(op->internal_ptr);
401
1/1
✓ Branch 8 → 9 taken 293141 times.
293141x op->invoke_handler();
402 293141x }
403
404 // wait_op completion handler
405
406 inline void
407 5x wait_op::do_complete(
408 void* owner,
409 scheduler_op* base,
410 std::uint32_t /*bytes*/,
411 std::uint32_t /*error*/)
412 {
413 5x auto* op = static_cast<wait_op*>(base);
414
415
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 5 times.
5x if (!owner)
416 {
417 op->cleanup_only();
418 op->internal_ptr.reset();
419 return;
420 }
421
422 5x auto prevent_premature_destruction = std::move(op->internal_ptr);
423
1/1
✓ Branch 8 → 9 taken 5 times.
5x op->invoke_handler();
424 5x }
425
426 // win_tcp_socket_internal
427
428 4280x inline win_tcp_socket_internal::win_tcp_socket_internal(win_tcp_service& svc) noexcept
429 4280x : svc_(svc)
430 4280x , conn_(*this)
431 4280x , rd_(*this)
432 4280x , wr_(*this)
433 8560x , wt_(*this)
434 {
435 4280x }
436
437 4280x inline win_tcp_socket_internal::~win_tcp_socket_internal()
438 {
439 4280x svc_.unregister_impl(*this);
440 4280x }
441
442 inline SOCKET
443 21077x win_tcp_socket_internal::native_handle() const noexcept
444 {
445 21077x return socket_;
446 }
447
448 inline endpoint
449 26x win_tcp_socket_internal::local_endpoint() const noexcept
450 {
451 26x return local_endpoint_;
452 }
453
454 inline endpoint
455 25x win_tcp_socket_internal::remote_endpoint() const noexcept
456 {
457 25x return remote_endpoint_;
458 }
459
460 inline bool
461 2291x win_tcp_socket_internal::is_open() const noexcept
462 {
463 2291x return socket_ != INVALID_SOCKET;
464 }
465
466 inline void
467 1372x win_tcp_socket_internal::set_socket(SOCKET s) noexcept
468 {
469 1372x socket_ = s;
470 1372x }
471
472 inline void
473 2745x win_tcp_socket_internal::set_endpoints(endpoint local, endpoint remote) noexcept
474 {
475 2745x local_endpoint_ = local;
476 2745x remote_endpoint_ = remote;
477 2745x }
478
479 inline std::coroutine_handle<>
480 1382x win_tcp_socket_internal::connect(
481 std::coroutine_handle<> h,
482 capy::executor_ref d,
483 endpoint ep,
484 std::stop_token token,
485 std::error_code* ec)
486 {
487 // Keep internal alive during I/O
488
1/1
✓ Branch 2 → 3 taken 1382 times.
1382x conn_.internal_ptr = shared_from_this();
489
490 1382x auto& op = conn_;
491 1382x op.reset();
492 1382x op.h = h;
493 1382x op.ex = d;
494 1382x op.ec_out = ec;
495 1382x op.target_endpoint = ep;
496 1382x op.start(token);
497
498 1382x svc_.work_started();
499
500 // ConnectEx requires the socket to be bound. Skip if already bound
501 // (e.g. the caller used tcp_socket::bind() before connect).
502
2/2
✓ Branch 10 → 11 taken 1381 times.
✓ Branch 10 → 22 taken 1 time.
1382x if (local_endpoint_ == endpoint{})
503 {
504 1381x sockaddr_storage bind_storage{};
505 socklen_t bind_len;
506
2/2
✓ Branch 11 → 12 taken 6 times.
✓ Branch 11 → 13 taken 1375 times.
1381x if (family_ == AF_INET6)
507 {
508 6x sockaddr_in6 sa6{};
509 6x sa6.sin6_family = AF_INET6;
510 6x sa6.sin6_port = 0;
511 6x sa6.sin6_addr = in6addr_any;
512 6x std::memcpy(&bind_storage, &sa6, sizeof(sa6));
513 6x bind_len = sizeof(sa6);
514 }
515 else
516 {
517 1375x sockaddr_in sa4{};
518 1375x sa4.sin_family = AF_INET;
519 1375x sa4.sin_addr.s_addr = INADDR_ANY;
520 1375x sa4.sin_port = 0;
521 1375x std::memcpy(&bind_storage, &sa4, sizeof(sa4));
522 1375x bind_len = sizeof(sa4);
523 }
524
525
1/1
✓ Branch 14 → 15 taken 1381 times.
1381x if (::bind(
526 socket_, reinterpret_cast<sockaddr*>(&bind_storage),
527
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 21 taken 1381 times.
1381x bind_len) == SOCKET_ERROR)
528 {
529 svc_.on_completion(&op, ::WSAGetLastError(), 0);
530 return std::noop_coroutine();
531 }
532 }
533
534 1382x auto connect_ex = svc_.connect_ex();
535
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 28 taken 1382 times.
1382x if (!connect_ex)
536 {
537 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
538 return std::noop_coroutine();
539 }
540
541 1382x sockaddr_storage storage{};
542 1382x socklen_t addrlen = detail::to_sockaddr(ep, family_, storage);
543
544
1/1
✓ Branch 29 → 30 taken 1382 times.
1382x BOOL result = connect_ex(
545 socket_, reinterpret_cast<sockaddr*>(&storage),
546 static_cast<int>(addrlen), nullptr, 0, nullptr, &op);
547
548
1/2
✓ Branch 30 → 31 taken 1382 times.
✗ Branch 30 → 37 not taken.
1382x if (!result)
549 {
550
1/1
✓ Branch 31 → 32 taken 1382 times.
1382x DWORD err = ::WSAGetLastError();
551
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 37 taken 1382 times.
1382x if (err != ERROR_IO_PENDING)
552 {
553 svc_.on_completion(&op, err, 0);
554 return std::noop_coroutine();
555 }
556 }
557
558 1382x svc_.on_pending(&op);
559 1382x return std::noop_coroutine();
560 }
561
562 inline std::coroutine_handle<>
563 294821x win_tcp_socket_internal::read_some(
564 std::coroutine_handle<> h,
565 capy::executor_ref d,
566 buffer_param param,
567 std::stop_token token,
568 std::error_code* ec,
569 std::size_t* bytes_out)
570 {
571 // Keep internal alive during I/O
572
1/1
✓ Branch 2 → 3 taken 294821 times.
294821x rd_.internal_ptr = shared_from_this();
573
574 294821x auto& op = rd_;
575 294821x op.reset();
576 294821x op.is_read = true;
577 294821x op.h = h;
578 294821x op.ex = d;
579 294821x op.ec_out = ec;
580 294821x op.bytes_out = bytes_out;
581 294821x op.start(token);
582
583 294821x svc_.work_started();
584
585 // Prepare buffers
586 294821x capy::mutable_buffer bufs[read_op::max_buffers];
587 294821x op.wsabuf_count =
588 294821x static_cast<DWORD>(param.copy_to(bufs, read_op::max_buffers));
589
590 // Handle empty buffer: complete with 0 bytes
591
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 14 taken 294820 times.
294821x if (op.wsabuf_count == 0)
592 {
593 1x op.empty_buffer = true;
594 1x svc_.on_completion(&op, 0, 0);
595 1x return std::noop_coroutine();
596 }
597
598
2/2
✓ Branch 18 → 15 taken 294820 times.
✓ Branch 18 → 19 taken 294820 times.
589640x for (DWORD i = 0; i < op.wsabuf_count; ++i)
599 {
600 294820x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
601 294820x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
602 }
603
604 294820x op.flags = 0;
605
606 589640x int result = ::WSARecv(
607
1/1
✓ Branch 19 → 20 taken 294820 times.
294820x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
608
609
2/2
✓ Branch 20 → 21 taken 3549 times.
✓ Branch 20 → 27 taken 291271 times.
294820x if (result == SOCKET_ERROR)
610 {
611
1/1
✓ Branch 21 → 22 taken 3549 times.
3549x DWORD err = ::WSAGetLastError();
612
2/2
✓ Branch 22 → 23 taken 10 times.
✓ Branch 22 → 27 taken 3539 times.
3549x if (err != WSA_IO_PENDING)
613 {
614 10x svc_.on_completion(&op, err, 0);
615 10x return std::noop_coroutine();
616 }
617 }
618
619 294810x svc_.on_pending(&op);
620
621 // Re-check cancellation after I/O is pending
622
2/2
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 294809 times.
294810x if (op.cancelled.load(std::memory_order_acquire))
623
1/1
✓ Branch 30 → 31 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
624
625 294810x return std::noop_coroutine();
626 }
627
628 inline std::coroutine_handle<>
629 293141x win_tcp_socket_internal::write_some(
630 std::coroutine_handle<> h,
631 capy::executor_ref d,
632 buffer_param param,
633 std::stop_token token,
634 std::error_code* ec,
635 std::size_t* bytes_out)
636 {
637 // Keep internal alive during I/O
638
1/1
✓ Branch 2 → 3 taken 293141 times.
293141x wr_.internal_ptr = shared_from_this();
639
640 293141x auto& op = wr_;
641 293141x op.reset();
642 293141x op.h = h;
643 293141x op.ex = d;
644 293141x op.ec_out = ec;
645 293141x op.bytes_out = bytes_out;
646 293141x op.start(token);
647
648 293141x svc_.work_started();
649
650 // Prepare buffers
651 293141x capy::mutable_buffer bufs[write_op::max_buffers];
652 293141x op.wsabuf_count =
653 293141x static_cast<DWORD>(param.copy_to(bufs, write_op::max_buffers));
654
655 // Handle empty buffer: complete immediately with 0 bytes
656
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 14 taken 293140 times.
293141x if (op.wsabuf_count == 0)
657 {
658 1x svc_.on_completion(&op, 0, 0);
659 1x return std::noop_coroutine();
660 }
661
662
2/2
✓ Branch 18 → 15 taken 293140 times.
✓ Branch 18 → 19 taken 293140 times.
586280x for (DWORD i = 0; i < op.wsabuf_count; ++i)
663 {
664 293140x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
665 293140x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
666 }
667
668 586280x int result = ::WSASend(
669
1/1
✓ Branch 19 → 20 taken 293140 times.
293140x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
670
671
2/2
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 27 taken 293139 times.
293140x if (result == SOCKET_ERROR)
672 {
673
1/1
✓ Branch 21 → 22 taken 1 time.
1x DWORD err = ::WSAGetLastError();
674
1/2
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 27 not taken.
1x if (err != WSA_IO_PENDING)
675 {
676 1x svc_.on_completion(&op, err, 0);
677 1x return std::noop_coroutine();
678 }
679 }
680
681 293139x svc_.on_pending(&op);
682
683 // Re-check cancellation after I/O is pending
684
2/2
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 293138 times.
293139x if (op.cancelled.load(std::memory_order_acquire))
685
1/1
✓ Branch 30 → 31 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
686
687 293139x return std::noop_coroutine();
688 }
689
690 inline std::coroutine_handle<>
691 5x win_tcp_socket_internal::wait(
692 std::coroutine_handle<> h,
693 capy::executor_ref d,
694 wait_type w,
695 std::stop_token token,
696 std::error_code* ec)
697 {
698
1/1
✓ Branch 2 → 3 taken 5 times.
5x wt_.internal_ptr = shared_from_this();
699
700 5x auto& op = wt_;
701 5x op.reset();
702 5x op.h = h;
703 5x op.ex = d;
704 5x op.ec_out = ec;
705 5x op.bytes_out = nullptr;
706 5x op.empty_buffer = true; // skip EOF translation in invoke_handler
707 5x op.start(token);
708
709 5x svc_.work_started();
710
711
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 3 times.
5x if (w == wait_type::write)
712 {
713 // Match asio's IOCP behavior and corosio's reactor contract:
714 // wait_type::write completes immediately on a connected socket.
715 2x svc_.on_completion(&op, 0, 0);
716 2x return std::noop_coroutine();
717 }
718
719
1/2
✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 29 not taken.
3x if (w == wait_type::read)
720 {
721 // Zero-byte WSARecv: kernel signals completion when data is
722 // available without consuming any bytes from the stream. This
723 // is the documented Winsock pattern for "is the socket
724 // readable" notifications.
725 3x op.wsabuf = WSABUF{0, nullptr};
726 3x op.flags = 0;
727
728 3x int result = ::WSARecv(
729 socket_, &op.wsabuf, 1, nullptr, &op.flags, &op, nullptr);
730
731
1/2
✓ Branch 15 → 16 taken 3 times.
✗ Branch 15 → 22 not taken.
3x if (result == SOCKET_ERROR)
732 {
733 3x DWORD err = ::WSAGetLastError();
734
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 22 taken 3 times.
3x if (err != WSA_IO_PENDING)
735 {
736 svc_.on_completion(&op, err, 0);
737 return std::noop_coroutine();
738 }
739 }
740
741 3x svc_.on_pending(&op);
742
743 // Re-check cancellation after I/O is pending.
744
2/2
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 26 taken 2 times.
3x if (op.cancelled.load(std::memory_order_acquire))
745 1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
746
747 3x return std::noop_coroutine();
748 }
749
750 // wait_type::error: route through the auxiliary select reactor.
751 svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
752 return std::noop_coroutine();
753 }
754
755 inline void
756 2144x win_tcp_socket_internal::cancel() noexcept
757 {
758
1/2
✓ Branch 2 → 3 taken 2144 times.
✗ Branch 2 → 4 not taken.
2144x if (socket_ != INVALID_SOCKET)
759 {
760 2144x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
761 }
762
763 2144x conn_.request_cancel();
764 2144x rd_.request_cancel();
765 2144x wr_.request_cancel();
766 2144x wt_.request_cancel();
767 // CancelIoEx covers overlapped I/O on the socket but cannot reach
768 // a wait op parked in the auxiliary reactor (no overlapped is
769 // outstanding). Route through the reactor explicitly. Safe no-op
770 // if the reactor was never constructed.
771 2144x svc_.scheduler().cancel_wait_if_constructed(&wt_);
772 2144x }
773
774 inline void
775 12736x win_tcp_socket_internal::close_socket() noexcept
776 {
777 // Tear down any aux-reactor-parked wait op before closing the
778 // SOCKET handle. Otherwise the reactor would keep polling a
779 // dangling fd (and on a Winsock SOCKET-id reuse the wrong fd
780 // could be polled briefly). The cancel happens-before the
781 // closesocket below.
782 12736x wt_.request_cancel();
783 12736x svc_.scheduler().cancel_wait_if_constructed(&wt_);
784
785
2/2
✓ Branch 5 → 6 taken 2781 times.
✓ Branch 5 → 9 taken 9955 times.
12736x if (socket_ != INVALID_SOCKET)
786 {
787 2781x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
788 2781x ::closesocket(socket_);
789 2781x socket_ = INVALID_SOCKET;
790 }
791
792 12736x family_ = AF_UNSPEC;
793
794 // Clear cached endpoints
795 12736x local_endpoint_ = endpoint{};
796 12736x remote_endpoint_ = endpoint{};
797 12736x }
798
799 // win_tcp_socket
800
801 4280x inline win_tcp_socket::win_tcp_socket(
802 4280x std::shared_ptr<win_tcp_socket_internal> internal) noexcept
803 4280x : internal_(std::move(internal))
804 {
805 4280x }
806
807 inline void
808 4280x win_tcp_socket::close_internal() noexcept
809 {
810
1/2
✓ Branch 3 → 4 taken 4280 times.
✗ Branch 3 → 7 not taken.
4280x if (internal_)
811 {
812 4280x internal_->close_socket();
813 4280x internal_.reset();
814 }
815 4280x }
816
817 inline std::coroutine_handle<>
818 1382x win_tcp_socket::connect(
819 std::coroutine_handle<> h,
820 capy::executor_ref d,
821 endpoint ep,
822 std::stop_token token,
823 std::error_code* ec)
824 {
825
1/1
✓ Branch 4 → 5 taken 1382 times.
1382x return internal_->connect(h, d, ep, token, ec);
826 }
827
828 inline std::coroutine_handle<>
829 294821x win_tcp_socket::read_some(
830 std::coroutine_handle<> h,
831 capy::executor_ref d,
832 buffer_param buf,
833 std::stop_token token,
834 std::error_code* ec,
835 std::size_t* bytes)
836 {
837
1/1
✓ Branch 4 → 5 taken 294821 times.
294821x return internal_->read_some(h, d, buf, token, ec, bytes);
838 }
839
840 inline std::coroutine_handle<>
841 293141x win_tcp_socket::write_some(
842 std::coroutine_handle<> h,
843 capy::executor_ref d,
844 buffer_param buf,
845 std::stop_token token,
846 std::error_code* ec,
847 std::size_t* bytes)
848 {
849
1/1
✓ Branch 4 → 5 taken 293141 times.
293141x return internal_->write_some(h, d, buf, token, ec, bytes);
850 }
851
852 inline std::coroutine_handle<>
853 5x win_tcp_socket::wait(
854 std::coroutine_handle<> h,
855 capy::executor_ref d,
856 wait_type w,
857 std::stop_token token,
858 std::error_code* ec)
859 {
860
1/1
✓ Branch 4 → 5 taken 5 times.
5x return internal_->wait(h, d, w, token, ec);
861 }
862
863 inline std::error_code
864 3x win_tcp_socket::shutdown(tcp_socket::shutdown_type what) noexcept
865 {
866 int how;
867
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)
868 {
869 1x case tcp_socket::shutdown_receive:
870 1x how = SD_RECEIVE;
871 1x break;
872 1x case tcp_socket::shutdown_send:
873 1x how = SD_SEND;
874 1x break;
875 1x case tcp_socket::shutdown_both:
876 1x how = SD_BOTH;
877 1x break;
878 default:
879 return make_err(WSAEINVAL);
880 }
881
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 3 times.
3x if (::shutdown(internal_->native_handle(), how) != 0)
882 return make_err(WSAGetLastError());
883 3x return {};
884 }
885
886 inline native_handle_type
887 14957x win_tcp_socket::native_handle() const noexcept
888 {
889 14957x return static_cast<native_handle_type>(internal_->native_handle());
890 }
891
892 inline std::error_code
893 2413x win_tcp_socket::set_option(
894 int level, int optname, void const* data, std::size_t size) noexcept
895 {
896 2413x if (::setsockopt(
897 2413x internal_->native_handle(), level, optname,
898
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 2413 times.
2413x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
899 return make_err(WSAGetLastError());
900 2413x return {};
901 }
902
903 inline std::error_code
904 40x win_tcp_socket::get_option(
905 int level, int optname, void* data, std::size_t* size) const noexcept
906 {
907 40x int len = static_cast<int>(*size);
908 40x if (::getsockopt(
909 40x internal_->native_handle(), level, optname,
910
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 40 times.
40x reinterpret_cast<char*>(data), &len) != 0)
911 return make_err(WSAGetLastError());
912 40x *size = static_cast<std::size_t>(len);
913 40x return {};
914 }
915
916 inline endpoint
917 26x win_tcp_socket::local_endpoint() const noexcept
918 {
919 26x return internal_->local_endpoint();
920 }
921
922 inline endpoint
923 25x win_tcp_socket::remote_endpoint() const noexcept
924 {
925 25x return internal_->remote_endpoint();
926 }
927
928 inline void
929 2144x win_tcp_socket::cancel() noexcept
930 {
931 2144x internal_->cancel();
932 2144x }
933
934 inline win_tcp_socket_internal*
935 11207x win_tcp_socket::get_internal() const noexcept
936 {
937 11207x return internal_.get();
938 }
939
940 // win_tcp_service
941
942 598x inline win_tcp_service::win_tcp_service(capy::execution_context& ctx)
943 1196x : sched_(ctx.use_service<win_scheduler>())
944
2/2
✓ Branch 5 → 6 taken 598 times.
✓ Branch 6 → 7 taken 598 times.
598x , iocp_(sched_.native_handle())
945 {
946
1/1
✓ Branch 12 → 13 taken 598 times.
598x load_extension_functions();
947 598x }
948
949 1196x inline win_tcp_service::~win_tcp_service()
950 {
951 // Delete wrappers that survived shutdown. This runs after
952 // win_scheduler is destroyed (reverse creation order), so
953 // all coroutine frames and their tcp_socket members are gone.
954
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 598 times.
598x for (auto* w = socket_wrapper_list_.pop_front(); w != nullptr;
955 w = socket_wrapper_list_.pop_front())
956 delete w;
957
958
1/2
✗ Branch 11 → 8 not taken.
✓ Branch 11 → 12 taken 598 times.
598x for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
959 w = acceptor_wrapper_list_.pop_front())
960 delete w;
961 1196x }
962
963 inline void
964 598x win_tcp_service::shutdown()
965 {
966 598x std::lock_guard<win_mutex> lock(mutex_);
967
968 // Close all sockets to force pending I/O to complete via IOCP.
969 // Wrappers are NOT deleted here - coroutine frames destroyed
970 // during scheduler shutdown may still hold tcp_socket objects
971 // that reference them. Wrapper deletion is deferred to ~win_tcp_service
972 // after the scheduler has drained all outstanding operations.
973
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 598 times.
598x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
974 impl = socket_list_.pop_front())
975 {
976 impl->close_socket();
977 }
978
979
1/2
✗ Branch 10 → 8 not taken.
✓ Branch 10 → 11 taken 598 times.
598x for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
980 impl = acceptor_list_.pop_front())
981 {
982 impl->close_socket();
983 }
984 598x }
985
986 inline io_object::implementation*
987 4280x win_tcp_service::construct()
988 {
989
1/1
✓ Branch 2 → 3 taken 4280 times.
4280x auto internal = std::make_shared<win_tcp_socket_internal>(*this);
990
991 {
992 4280x std::lock_guard<win_mutex> lock(mutex_);
993 4280x socket_list_.push_back(internal.get());
994 4280x }
995
996
1/1
✓ Branch 7 → 8 taken 4280 times.
4280x auto* wrapper = new win_tcp_socket(std::move(internal));
997
998 {
999 4280x std::lock_guard<win_mutex> lock(mutex_);
1000 4280x socket_wrapper_list_.push_back(wrapper);
1001 4280x }
1002
1003 4280x return wrapper;
1004 4280x }
1005
1006 inline void
1007 4280x win_tcp_service::destroy(io_object::implementation* p)
1008 {
1009
1/2
✓ Branch 2 → 3 taken 4280 times.
✗ Branch 2 → 5 not taken.
4280x if (p)
1010 {
1011 4280x auto& wrapper = static_cast<win_tcp_socket&>(*p);
1012 4280x wrapper.close_internal();
1013 4280x destroy_impl(wrapper);
1014 }
1015 4280x }
1016
1017 inline void
1018 7047x win_tcp_service::close(io_object::handle& h)
1019 {
1020 7047x auto& wrapper = static_cast<win_tcp_socket&>(*h.get());
1021 7047x wrapper.get_internal()->close_socket();
1022 7047x }
1023
1024 inline void
1025 4280x win_tcp_service::destroy_impl(win_tcp_socket& impl)
1026 {
1027 {
1028 4280x std::lock_guard<win_mutex> lock(mutex_);
1029 4280x socket_wrapper_list_.remove(&impl);
1030 4280x }
1031
1/2
✓ Branch 5 → 6 taken 4280 times.
✗ Branch 5 → 7 not taken.
4280x delete &impl;
1032 4280x }
1033
1034 inline void
1035 4280x win_tcp_service::unregister_impl(win_tcp_socket_internal& impl)
1036 {
1037 4280x std::lock_guard<win_mutex> lock(mutex_);
1038 4280x socket_list_.remove(&impl);
1039 4280x }
1040
1041 inline std::error_code
1042 1409x win_tcp_service::open_socket(
1043 win_tcp_socket_internal& impl, int family, int type, int protocol)
1044 {
1045 1409x impl.close_socket();
1046
1047 SOCKET sock =
1048 1409x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1049
1050
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1409 times.
1409x if (sock == INVALID_SOCKET)
1051 return make_err(::WSAGetLastError());
1052
1053
2/2
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 10 taken 1401 times.
1409x if (family == AF_INET6)
1054 {
1055 8x DWORD one = 1;
1056
1/1
✓ Branch 8 → 9 taken 8 times.
8x ::setsockopt(
1057 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
1058 sizeof(one));
1059 }
1060
1061 2818x HANDLE result = ::CreateIoCompletionPort(
1062 1409x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1063
1064
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1409 times.
1409x if (result == nullptr)
1065 {
1066 DWORD dwError = ::GetLastError();
1067 ::closesocket(sock);
1068 return make_err(dwError);
1069 }
1070
1071 1409x impl.socket_ = sock;
1072 1409x impl.family_ = family;
1073 1409x return {};
1074 }
1075
1076 inline std::error_code
1077 7x win_tcp_service::bind_socket(win_tcp_socket_internal& impl, endpoint ep)
1078 {
1079 7x SOCKET sock = impl.socket_;
1080
1081 7x sockaddr_storage storage{};
1082 7x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1083
1/1
✓ Branch 3 → 4 taken 7 times.
7x if (::bind(
1084 sock, reinterpret_cast<sockaddr*>(&storage),
1085
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 5 times.
7x static_cast<int>(addrlen)) == SOCKET_ERROR)
1086
1/1
✓ Branch 5 → 6 taken 2 times.
2x return make_err(::WSAGetLastError());
1087
1088 // Cache local endpoint (resolves ephemeral port)
1089 5x sockaddr_storage local_storage{};
1090 5x int local_len = sizeof(local_storage);
1091
1/1
✓ Branch 7 → 8 taken 5 times.
5x if (::getsockname(
1092
1/2
✓ Branch 8 → 9 taken 5 times.
✗ Branch 8 → 11 not taken.
5x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1093 5x impl.local_endpoint_ = detail::from_sockaddr(local_storage);
1094
1095 5x return {};
1096 }
1097
1098 inline void*
1099 1386x win_tcp_service::native_handle() const noexcept
1100 {
1101 1386x return iocp_;
1102 }
1103
1104 inline void
1105 win_tcp_service::post(overlapped_op* op)
1106 {
1107 sched_.post(op);
1108 }
1109
1110 inline void
1111 590720x win_tcp_service::on_pending(overlapped_op* op) noexcept
1112 {
1113 590720x sched_.on_pending(op);
1114 590720x }
1115
1116 inline void
1117 15x win_tcp_service::on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept
1118 {
1119 15x sched_.on_completion(op, error, bytes);
1120 15x }
1121
1122 inline void
1123 590737x win_tcp_service::work_started() noexcept
1124 {
1125 590737x sched_.work_started();
1126 590737x }
1127
1128 inline void
1129 win_tcp_service::work_finished() noexcept
1130 {
1131 sched_.work_finished();
1132 }
1133
1134 inline void
1135 598x win_tcp_service::load_extension_functions()
1136 {
1137
1/1
✓ Branch 2 → 3 taken 598 times.
598x SOCKET sock = ::WSASocketW(
1138 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1139
1140
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 598 times.
598x if (sock == INVALID_SOCKET)
1141 return;
1142
1143 598x DWORD bytes = 0;
1144
1145 598x GUID connect_ex_guid = WSAID_CONNECTEX;
1146 598x ::WSAIoctl(
1147 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
1148
1/1
✓ Branch 5 → 6 taken 598 times.
598x sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
1149 nullptr, nullptr);
1150
1151 598x GUID accept_ex_guid = WSAID_ACCEPTEX;
1152 598x ::WSAIoctl(
1153 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
1154
1/1
✓ Branch 6 → 7 taken 598 times.
598x sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
1155 nullptr, nullptr);
1156
1157
1/1
✓ Branch 7 → 8 taken 598 times.
598x ::closesocket(sock);
1158 }
1159
1160 inline void
1161 1349x win_tcp_service::destroy_acceptor_impl(win_tcp_acceptor& impl)
1162 {
1163 {
1164 1349x std::lock_guard<win_mutex> lock(mutex_);
1165 1349x acceptor_wrapper_list_.remove(&impl);
1166 1349x }
1167
1/2
✓ Branch 5 → 6 taken 1349 times.
✗ Branch 5 → 7 not taken.
1349x delete &impl;
1168 1349x }
1169
1170 inline void
1171 1349x win_tcp_service::unregister_acceptor_impl(win_tcp_acceptor_internal& impl)
1172 {
1173 1349x std::lock_guard<win_mutex> lock(mutex_);
1174 1349x acceptor_list_.remove(&impl);
1175 1349x }
1176
1177 inline std::error_code
1178 1343x win_tcp_service::open_acceptor_socket(
1179 win_tcp_acceptor_internal& impl, int family, int type, int protocol)
1180 {
1181 1343x impl.close_socket();
1182
1183 SOCKET sock =
1184 1343x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1185
1186
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1343 times.
1343x if (sock == INVALID_SOCKET)
1187 return make_err(::WSAGetLastError());
1188
1189
2/2
✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 10 taken 1333 times.
1343x if (family == AF_INET6)
1190 {
1191 10x DWORD val = 0; // dual-stack default
1192
1/1
✓ Branch 8 → 9 taken 10 times.
10x ::setsockopt(
1193 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&val),
1194 sizeof(val));
1195 }
1196
1197 2686x HANDLE result = ::CreateIoCompletionPort(
1198 1343x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1199
1200
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1343 times.
1343x if (result == nullptr)
1201 {
1202 DWORD dwError = ::GetLastError();
1203 ::closesocket(sock);
1204 return make_err(dwError);
1205 }
1206
1207 1343x impl.socket_ = sock;
1208 1343x return {};
1209 }
1210
1211 inline std::error_code
1212 1342x win_tcp_service::bind_acceptor(win_tcp_acceptor_internal& impl, endpoint ep)
1213 {
1214 1342x SOCKET sock = impl.socket_;
1215
1216 1342x sockaddr_storage storage{};
1217 1342x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1218
1/1
✓ Branch 3 → 4 taken 1342 times.
1342x if (::bind(
1219 sock, reinterpret_cast<sockaddr*>(&storage),
1220
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 7 taken 1338 times.
1342x static_cast<int>(addrlen)) == SOCKET_ERROR)
1221
1/1
✓ Branch 5 → 6 taken 4 times.
4x return make_err(::WSAGetLastError());
1222
1223 // Cache local endpoint (resolves ephemeral port)
1224 1338x sockaddr_storage local_storage{};
1225 1338x int local_len = sizeof(local_storage);
1226
1/1
✓ Branch 7 → 8 taken 1338 times.
1338x if (::getsockname(
1227
1/2
✓ Branch 8 → 9 taken 1338 times.
✗ Branch 8 → 11 not taken.
1338x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1228 1338x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1229
1230 1338x return {};
1231 }
1232
1233 inline std::error_code
1234 1331x win_tcp_service::listen_acceptor(win_tcp_acceptor_internal& impl, int backlog)
1235 {
1236 1331x SOCKET sock = impl.socket_;
1237
1238
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1331 times.
1331x if (::listen(sock, backlog) == SOCKET_ERROR)
1239 return make_err(::WSAGetLastError());
1240
1241 1331x return {};
1242 }
1243
1244 // win_tcp_acceptor_internal
1245
1246 1349x inline win_tcp_acceptor_internal::win_tcp_acceptor_internal(win_tcp_service& svc) noexcept
1247 1349x : svc_(svc)
1248 {
1249 1349x }
1250
1251 1349x inline win_tcp_acceptor_internal::~win_tcp_acceptor_internal()
1252 {
1253 1349x svc_.unregister_acceptor_impl(*this);
1254 1349x }
1255
1256 inline win_tcp_service&
1257 14x win_tcp_acceptor_internal::socket_service() noexcept
1258 {
1259 14x return svc_;
1260 }
1261
1262 inline SOCKET
1263 1340x win_tcp_acceptor_internal::native_handle() const noexcept
1264 {
1265 1340x return socket_;
1266 }
1267
1268 inline endpoint
1269 1328x win_tcp_acceptor_internal::local_endpoint() const noexcept
1270 {
1271 1328x return local_endpoint_;
1272 }
1273
1274 inline bool
1275 10766x win_tcp_acceptor_internal::is_open() const noexcept
1276 {
1277 10766x return socket_ != INVALID_SOCKET;
1278 }
1279
1280 inline void
1281 1338x win_tcp_acceptor_internal::set_local_endpoint(endpoint ep) noexcept
1282 {
1283 1338x local_endpoint_ = ep;
1284 1338x }
1285
1286 inline std::coroutine_handle<>
1287 1386x win_tcp_acceptor_internal::accept(
1288 std::coroutine_handle<> h,
1289 capy::executor_ref d,
1290 std::stop_token token,
1291 std::error_code* ec,
1292 io_object::implementation** impl_out)
1293 {
1294 // Keep acceptor internal alive during I/O
1295
1/1
✓ Branch 2 → 3 taken 1386 times.
1386x acc_.acceptor_ptr = shared_from_this();
1296
1297 1386x auto& op = acc_;
1298 1386x op.reset();
1299 1386x op.h = h;
1300 1386x op.ex = d;
1301 1386x op.ec_out = ec;
1302 1386x op.impl_out = impl_out;
1303 1386x op.start(token);
1304
1305 1386x svc_.work_started();
1306
1307 // Create wrapper for the peer socket (service owns it)
1308
1/1
✓ Branch 8 → 9 taken 1386 times.
1386x auto& peer_wrapper = static_cast<win_tcp_socket&>(*svc_.construct());
1309
1310 // Derive AF from the listening socket's cached local endpoint
1311
2/2
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 12 taken 1379 times.
1386x int af = local_endpoint_.is_v6() ? AF_INET6 : AF_INET;
1312
1313 // Create the accepted socket with matching address family
1314
1/1
✓ Branch 13 → 14 taken 1386 times.
1386x SOCKET accepted = ::WSASocketW(
1315 af, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1316
1317
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 21 taken 1386 times.
1386x if (accepted == INVALID_SOCKET)
1318 {
1319 svc_.destroy(&peer_wrapper);
1320 svc_.on_completion(&op, ::WSAGetLastError(), 0);
1321 return std::noop_coroutine();
1322 }
1323
1324
1/1
✓ Branch 22 → 23 taken 1386 times.
1386x HANDLE result = ::CreateIoCompletionPort(
1325 1386x reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
1326
1327
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 31 taken 1386 times.
1386x if (result == nullptr)
1328 {
1329 DWORD err = ::GetLastError();
1330 ::closesocket(accepted);
1331 svc_.destroy(&peer_wrapper);
1332 svc_.on_completion(&op, err, 0);
1333 return std::noop_coroutine();
1334 }
1335
1336 // Set up the accept operation
1337 1386x op.accepted_socket = accepted;
1338 1386x op.peer_wrapper = &peer_wrapper;
1339 1386x op.listen_socket = socket_;
1340
1341 1386x auto accept_ex = svc_.accept_ex();
1342
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 39 taken 1386 times.
1386x if (!accept_ex)
1343 {
1344 ::closesocket(accepted);
1345 svc_.destroy(&peer_wrapper);
1346 op.peer_wrapper = nullptr;
1347 op.accepted_socket = INVALID_SOCKET;
1348 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
1349 return std::noop_coroutine();
1350 }
1351
1352 // AcceptEx address buffer sizes must match the socket's address family
1353
2/2
✓ Branch 39 → 40 taken 7 times.
✓ Branch 39 → 41 taken 1379 times.
1386x DWORD addr_size = static_cast<DWORD>(
1354 (af == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) + 16);
1355 1386x DWORD bytes_received = 0;
1356
1357 2772x BOOL ok = accept_ex(
1358
1/1
✓ Branch 42 → 43 taken 1386 times.
1386x socket_, accepted, op.addr_buf, 0, addr_size, addr_size,
1359 &bytes_received, &op);
1360
1361
1/2
✓ Branch 43 → 44 taken 1386 times.
✗ Branch 43 → 52 not taken.
1386x if (!ok)
1362 {
1363
1/1
✓ Branch 44 → 45 taken 1386 times.
1386x DWORD err = ::WSAGetLastError();
1364
1/2
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 52 taken 1386 times.
1386x if (err != ERROR_IO_PENDING)
1365 {
1366 ::closesocket(accepted);
1367 svc_.destroy(&peer_wrapper);
1368 op.peer_wrapper = nullptr;
1369 op.accepted_socket = INVALID_SOCKET;
1370 svc_.on_completion(&op, err, 0);
1371 return std::noop_coroutine();
1372 }
1373 }
1374
1375 1386x svc_.on_pending(&op);
1376
1377 // If the stop_token was already cancelled when start() was called,
1378 // the CancelIoEx in the canceller fired before AcceptEx was
1379 // submitted and had no effect. Re-issue now that the I/O is
1380 // pending so the completion posts with ERROR_OPERATION_ABORTED.
1381
2/2
✓ Branch 54 → 55 taken 2 times.
✓ Branch 54 → 56 taken 1384 times.
1386x if (op.cancelled.load(std::memory_order_acquire))
1382
1/1
✓ Branch 55 → 56 taken 2 times.
2x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
1383
1384 1386x return std::noop_coroutine();
1385 }
1386
1387 inline std::coroutine_handle<>
1388 2x win_tcp_acceptor_internal::wait(
1389 std::coroutine_handle<> h,
1390 capy::executor_ref d,
1391 wait_type w,
1392 std::stop_token token,
1393 std::error_code* ec)
1394 {
1395
1/1
✓ Branch 2 → 3 taken 2 times.
2x wt_.acceptor_ptr = shared_from_this();
1396 2x wt_.listen_socket = socket_;
1397
1398 2x auto& op = wt_;
1399 2x op.reset();
1400 2x op.h = h;
1401 2x op.ex = d;
1402 2x op.ec_out = ec;
1403 2x op.bytes_out = nullptr;
1404 2x op.start(token);
1405
1406 2x svc_.work_started();
1407
1408
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 13 taken 2 times.
2x if (w == wait_type::write)
1409 {
1410 svc_.on_completion(&op, 0, 0);
1411 return std::noop_coroutine();
1412 }
1413
1414 // wait_type::read (incoming connection ready) and wait_type::error
1415 // on the listen socket route through the auxiliary select reactor.
1416 2x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
1417 2x return std::noop_coroutine();
1418 }
1419
1420 inline void
1421 3x win_tcp_acceptor_internal::cancel() noexcept
1422 {
1423
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 4 not taken.
3x if (socket_ != INVALID_SOCKET)
1424 {
1425 3x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1426 }
1427
1428 3x acc_.request_cancel();
1429 3x wt_.request_cancel();
1430 3x svc_.scheduler().cancel_wait_if_constructed(&wt_);
1431 3x }
1432
1433 inline void
1434 5384x win_tcp_acceptor_internal::close_socket() noexcept
1435 {
1436 // Tear down any aux-reactor-parked wait op first.
1437 5384x wt_.request_cancel();
1438 5384x svc_.scheduler().cancel_wait_if_constructed(&wt_);
1439
1440
2/2
✓ Branch 5 → 6 taken 1343 times.
✓ Branch 5 → 9 taken 4041 times.
5384x if (socket_ != INVALID_SOCKET)
1441 {
1442 1343x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1443 1343x ::closesocket(socket_);
1444 1343x socket_ = INVALID_SOCKET;
1445 }
1446
1447 // Clear cached endpoint
1448 5384x local_endpoint_ = endpoint{};
1449 5384x }
1450
1451 // win_tcp_acceptor
1452
1453 1349x inline win_tcp_acceptor::win_tcp_acceptor(
1454 1349x std::shared_ptr<win_tcp_acceptor_internal> internal) noexcept
1455 1349x : internal_(std::move(internal))
1456 {
1457 1349x }
1458
1459 inline void
1460 1349x win_tcp_acceptor::close_internal() noexcept
1461 {
1462
1/2
✓ Branch 3 → 4 taken 1349 times.
✗ Branch 3 → 7 not taken.
1349x if (internal_)
1463 {
1464 1349x internal_->close_socket();
1465 1349x internal_.reset();
1466 }
1467 1349x }
1468
1469 inline std::coroutine_handle<>
1470 1386x win_tcp_acceptor::accept(
1471 std::coroutine_handle<> h,
1472 capy::executor_ref d,
1473 std::stop_token token,
1474 std::error_code* ec,
1475 io_object::implementation** impl_out)
1476 {
1477
1/1
✓ Branch 4 → 5 taken 1386 times.
1386x return internal_->accept(h, d, token, ec, impl_out);
1478 }
1479
1480 inline std::coroutine_handle<>
1481 2x win_tcp_acceptor::wait(
1482 std::coroutine_handle<> h,
1483 capy::executor_ref d,
1484 wait_type w,
1485 std::stop_token token,
1486 std::error_code* ec)
1487 {
1488
1/1
✓ Branch 4 → 5 taken 2 times.
2x return internal_->wait(h, d, w, token, ec);
1489 }
1490
1491 inline endpoint
1492 1328x win_tcp_acceptor::local_endpoint() const noexcept
1493 {
1494 1328x return internal_->local_endpoint();
1495 }
1496
1497 inline bool
1498 10766x win_tcp_acceptor::is_open() const noexcept
1499 {
1500
3/4
✓ Branch 3 → 4 taken 10766 times.
✗ Branch 3 → 8 not taken.
✓ Branch 6 → 7 taken 8093 times.
✓ Branch 6 → 8 taken 2673 times.
10766x return internal_ && internal_->is_open();
1501 }
1502
1503 inline void
1504 3x win_tcp_acceptor::cancel() noexcept
1505 {
1506 3x internal_->cancel();
1507 3x }
1508
1509 inline std::error_code
1510 1340x win_tcp_acceptor::set_option(
1511 int level, int optname, void const* data, std::size_t size) noexcept
1512 {
1513 1340x if (::setsockopt(
1514 1340x internal_->native_handle(), level, optname,
1515
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1340 times.
1340x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
1516 return make_err(WSAGetLastError());
1517 1340x return {};
1518 }
1519
1520 inline std::error_code
1521 win_tcp_acceptor::get_option(
1522 int level, int optname, void* data, std::size_t* size) const noexcept
1523 {
1524 int len = static_cast<int>(*size);
1525 if (::getsockopt(
1526 internal_->native_handle(), level, optname,
1527 reinterpret_cast<char*>(data), &len) != 0)
1528 return make_err(WSAGetLastError());
1529 *size = static_cast<std::size_t>(len);
1530 return {};
1531 }
1532
1533 inline win_tcp_acceptor_internal*
1534 6708x win_tcp_acceptor::get_internal() const noexcept
1535 {
1536 6708x return internal_.get();
1537 }
1538
1539 // win_tcp_acceptor_service
1540
1541 598x inline win_tcp_acceptor_service::win_tcp_acceptor_service(
1542 598x capy::execution_context& ctx, win_tcp_service& svc)
1543 598x : svc_(svc)
1544 {
1545 (void)ctx;
1546 598x }
1547
1548 inline io_object::implementation*
1549 1349x win_tcp_acceptor_service::construct()
1550 {
1551
1/1
✓ Branch 2 → 3 taken 1349 times.
1349x auto internal = std::make_shared<win_tcp_acceptor_internal>(svc_);
1552
1553 {
1554 1349x std::lock_guard<win_mutex> lock(svc_.mutex_);
1555 1349x svc_.acceptor_list_.push_back(internal.get());
1556 1349x }
1557
1558
1/1
✓ Branch 7 → 8 taken 1349 times.
1349x auto* wrapper = new win_tcp_acceptor(std::move(internal));
1559
1560 {
1561 1349x std::lock_guard<win_mutex> lock(svc_.mutex_);
1562 1349x svc_.acceptor_wrapper_list_.push_back(wrapper);
1563 1349x }
1564
1565 1349x return wrapper;
1566 1349x }
1567
1568 inline void
1569 1349x win_tcp_acceptor_service::destroy(io_object::implementation* p)
1570 {
1571
1/2
✓ Branch 2 → 3 taken 1349 times.
✗ Branch 2 → 5 not taken.
1349x if (p)
1572 {
1573 1349x auto& wrapper = static_cast<win_tcp_acceptor&>(*p);
1574 1349x wrapper.close_internal();
1575 1349x svc_.destroy_acceptor_impl(wrapper);
1576 }
1577 1349x }
1578
1579 inline void
1580 2692x win_tcp_acceptor_service::close(io_object::handle& h)
1581 {
1582 2692x auto& wrapper = static_cast<win_tcp_acceptor&>(*h.get());
1583 2692x wrapper.get_internal()->close_socket();
1584 2692x }
1585
1586 inline std::error_code
1587 1343x win_tcp_acceptor_service::open_acceptor_socket(
1588 tcp_acceptor::implementation& impl, int family, int type, int protocol)
1589 {
1590 1343x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1591 1343x return svc_.open_acceptor_socket(
1592 2686x *wrapper.get_internal(), family, type, protocol);
1593 }
1594
1595 inline std::error_code
1596 1342x win_tcp_acceptor_service::bind_acceptor(
1597 tcp_acceptor::implementation& impl, endpoint ep)
1598 {
1599 1342x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1600 1342x return svc_.bind_acceptor(*wrapper.get_internal(), ep);
1601 }
1602
1603 inline std::error_code
1604 1331x win_tcp_acceptor_service::listen_acceptor(
1605 tcp_acceptor::implementation& impl, int backlog)
1606 {
1607 1331x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1608 1331x return svc_.listen_acceptor(*wrapper.get_internal(), backlog);
1609 }
1610
1611 inline void
1612 598x win_tcp_acceptor_service::shutdown()
1613 {
1614 598x }
1615
1616 } // namespace boost::corosio::detail
1617
1618 #endif // BOOST_COROSIO_HAS_IOCP
1619
1620 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
1621