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

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