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

87.4% Lines (645/738) 97.9% List of functions (93/95) 66.6% Branches (191/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 4241x 100.0% 100.0% boost::corosio::detail::read_op::read_op(boost::corosio::detail::win_tcp_socket_internal&) :83 4241x 100.0% 100.0% boost::corosio::detail::write_op::write_op(boost::corosio::detail::win_tcp_socket_internal&) :90 4241x 100.0% 100.0% boost::corosio::detail::wait_op::wait_op(boost::corosio::detail::win_tcp_socket_internal&) :97 4241x 100.0% 100.0% boost::corosio::detail::accept_op::accept_op() :104 1340x 100.0% 100.0% boost::corosio::detail::acceptor_wait_op::acceptor_wait_op() :109 1340x 100.0% 100.0% boost::corosio::detail::connect_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :118 0 0.0% 0.0% 0.0% boost::corosio::detail::read_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :129 902x 100.0% 50.0% 88.9% boost::corosio::detail::write_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :141 3x 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 11x 100.0% 50.0% 83.3% boost::corosio::detail::acceptor_wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :183 1x 100.0% 50.0% 92.3% boost::corosio::detail::accept_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :201 1375x 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 5x 66.7% 66.7% 61.5% boost::corosio::detail::connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :322 1373x 85.7% 76.5% 82.9% boost::corosio::detail::read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :363 213877x 66.7% 66.7% 61.5% boost::corosio::detail::write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :385 212160x 66.7% 66.7% 61.5% boost::corosio::detail::wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :407 9x 66.7% 66.7% 61.5% boost::corosio::detail::win_tcp_socket_internal::win_tcp_socket_internal(boost::corosio::detail::win_tcp_service&) :428 4241x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::~win_tcp_socket_internal() :437 4241x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::native_handle() const :443 20811x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::local_endpoint() const :449 24x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::remote_endpoint() const :455 23x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::is_open() const :461 2269x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_socket(unsigned long long) :467 1362x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_endpoints(boost::corosio::endpoint, boost::corosio::endpoint) :473 2725x 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 1373x 86.0% 70.6% 67.4% boost::corosio::detail::win_tcp_socket_internal::read_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :563 213877x 97.0% 85.7% 94.7% boost::corosio::detail::win_tcp_socket_internal::write_some(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :629 212160x 96.7% 78.6% 94.7% 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 9x 89.7% 72.7% 86.5% boost::corosio::detail::win_tcp_socket_internal::cancel() :756 2112x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::close_socket() :775 12627x 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 4241x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::close_internal() :808 4241x 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 1373x 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 213877x 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 212160x 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 9x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::shutdown(boost::corosio::shutdown_type) :864 5x 81.2% 66.7% 76.9% boost::corosio::detail::win_tcp_socket::native_handle() const :887 14765x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::set_option(int, int, void const*, unsigned long long) :893 2376x 83.3% 50.0% 75.0% boost::corosio::detail::win_tcp_socket::get_option(int, int, void*, unsigned long long*) const :904 33x 87.5% 50.0% 77.8% boost::corosio::detail::win_tcp_socket::local_endpoint() const :917 24x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::remote_endpoint() const :923 23x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::cancel() :929 2112x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::get_internal() const :935 11116x 100.0% 100.0% boost::corosio::detail::win_tcp_service::win_tcp_service(boost::capy::execution_context&) :942 594x 100.0% 100.0% 65.0% boost::corosio::detail::win_tcp_service::~win_tcp_service() :949 1188x 100.0% 100.0% boost::corosio::detail::win_tcp_service::shutdown() :964 594x 55.6% 50.0% 63.6% boost::corosio::detail::win_tcp_service::construct() :987 4241x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_service::destroy(boost::corosio::io_object::implementation*) :1007 4241x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::close(boost::corosio::io_object::handle&) :1018 6988x 100.0% 100.0% boost::corosio::detail::win_tcp_service::destroy_impl(boost::corosio::detail::win_tcp_socket&) :1025 4241x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_impl(boost::corosio::detail::win_tcp_socket_internal&) :1035 4241x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_socket(boost::corosio::detail::win_tcp_socket_internal&, int, int, int) :1042 1398x 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 6x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::native_handle() const :1099 1375x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_pending(boost::corosio::detail::overlapped_op*) :1111 428782x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1117 9x 100.0% 100.0% boost::corosio::detail::win_tcp_service::work_started() :1123 428799x 100.0% 100.0% boost::corosio::detail::win_tcp_service::load_extension_functions() :1135 594x 91.7% 83.3% 88.9% boost::corosio::detail::win_tcp_service::destroy_acceptor_impl(boost::corosio::detail::win_tcp_acceptor&) :1161 1340x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_acceptor_impl(boost::corosio::detail::win_tcp_acceptor_internal&) :1171 1340x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_acceptor_socket(boost::corosio::detail::win_tcp_acceptor_internal&, int, int, int) :1178 1335x 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 1334x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::listen_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, int) :1234 1323x 80.0% 50.0% 66.7% boost::corosio::detail::win_tcp_acceptor_internal::win_tcp_acceptor_internal(boost::corosio::detail::win_tcp_service&) :1246 1340x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::~win_tcp_acceptor_internal() :1251 1340x 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 1332x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::local_endpoint() const :1269 1318x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::is_open() const :1275 10693x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::set_local_endpoint(boost::corosio::endpoint) :1281 1330x 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 1375x 62.3% 56.2% 58.7% 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 5x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::cancel() :1421 4x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::close_socket() :1434 5350x 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 1340x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::close_internal() :1460 1340x 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 1375x 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 5x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::local_endpoint() const :1492 1318x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::is_open() const :1498 10693x 100.0% 75.0% 100.0% boost::corosio::detail::win_tcp_acceptor::cancel() :1504 4x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::set_option(int, int, void const*, unsigned long long) :1510 1332x 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 6667x 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 594x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::construct() :1549 1340x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_acceptor_service::destroy(boost::corosio::io_object::implementation*) :1569 1340x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::close(boost::corosio::io_object::handle&) :1580 2675x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::open_acceptor_socket(boost::corosio::tcp_acceptor::implementation&, int, int, int) :1587 1335x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::bind_acceptor(boost::corosio::tcp_acceptor::implementation&, boost::corosio::endpoint) :1596 1334x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::listen_acceptor(boost::corosio::tcp_acceptor::implementation&, int) :1604 1323x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::shutdown() :1612 594x 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 4241x inline connect_op::connect_op(win_tcp_socket_internal& internal_) noexcept
77 : overlapped_op(&do_complete)
78 4241x , internal(internal_)
79 {
80 4241x cancel_func_ = &do_cancel_impl;
81 4241x }
82
83 4241x inline read_op::read_op(win_tcp_socket_internal& internal_) noexcept
84 : overlapped_op(&do_complete)
85 4241x , internal(internal_)
86 {
87 4241x cancel_func_ = &do_cancel_impl;
88 4241x }
89
90 4241x inline write_op::write_op(win_tcp_socket_internal& internal_) noexcept
91 : overlapped_op(&do_complete)
92 4241x , internal(internal_)
93 {
94 4241x cancel_func_ = &do_cancel_impl;
95 4241x }
96
97 4241x inline wait_op::wait_op(win_tcp_socket_internal& internal_) noexcept
98 : overlapped_op(&do_complete)
99 4241x , internal(internal_)
100 {
101 4241x cancel_func_ = &do_cancel_impl;
102 4241x }
103
104 1340x inline accept_op::accept_op() noexcept : overlapped_op(&do_complete)
105 {
106 1340x cancel_func_ = &do_cancel_impl;
107 1340x }
108
109 1340x inline acceptor_wait_op::acceptor_wait_op() noexcept
110 1340x : overlapped_op(&do_complete)
111 {
112 1340x cancel_func_ = &do_cancel_impl;
113 1340x }
114
115 // Cancellation functions
116
117 inline void
118 connect_op::do_cancel_impl(overlapped_op* base) noexcept
119 {
120 auto* op = static_cast<connect_op*>(base);
121 if (op->internal.is_open())
122 {
123 ::CancelIoEx(
124 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
125 }
126 }
127
128 inline void
129 902x read_op::do_cancel_impl(overlapped_op* base) noexcept
130 {
131 902x auto* op = static_cast<read_op*>(base);
132 902x op->cancelled.store(true, std::memory_order_release);
133
1/2
✓ Branch 4 → 5 taken 902 times.
✗ Branch 4 → 10 not taken.
902x if (op->internal.is_open())
134 {
135
1/2
✓ Branch 5 → 6 taken 902 times.
✗ Branch 5 → 7 not taken.
1804x ::CancelIoEx(
136 902x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
137 }
138 902x }
139
140 inline void
141 3x write_op::do_cancel_impl(overlapped_op* base) noexcept
142 {
143 3x auto* op = static_cast<write_op*>(base);
144 3x op->cancelled.store(true, std::memory_order_release);
145
1/2
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 10 not taken.
3x if (op->internal.is_open())
146 {
147
1/2
✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 7 not taken.
6x ::CancelIoEx(
148 3x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
149 }
150 3x }
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 11x accept_op::do_cancel_impl(overlapped_op* base) noexcept
174 {
175 11x auto* op = static_cast<accept_op*>(base);
176
1/2
✓ Branch 2 → 3 taken 11 times.
✗ Branch 2 → 7 not taken.
11x 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 11x }
181
182 inline void
183 1x acceptor_wait_op::do_cancel_impl(overlapped_op* base) noexcept
184 {
185 1x auto* op = static_cast<acceptor_wait_op*>(base);
186 1x op->cancelled.store(true, std::memory_order_release);
187
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
1x if (op->listen_socket != INVALID_SOCKET)
188 {
189
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
190 }
191
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 14 not taken.
1x if (op->acceptor_ptr)
192 {
193 1x op->acceptor_ptr->socket_service().scheduler()
194 1x .cancel_wait_if_constructed(op);
195 }
196 1x }
197
198 // accept_op completion handler
199
200 inline void
201 1375x accept_op::do_complete(
202 void* owner,
203 scheduler_op* base,
204 std::uint32_t /*bytes*/,
205 std::uint32_t /*error*/)
206 {
207 1375x auto* op = static_cast<accept_op*>(base);
208
209
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 1375 times.
1375x 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 1375x op->stop_cb.reset();
229
230 bool success =
231
3/4
✓ Branch 13 → 14 taken 1362 times.
✓ Branch 13 → 17 taken 13 times.
✓ Branch 15 → 16 taken 1362 times.
✗ Branch 15 → 17 not taken.
1375x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
232
233
1/2
✓ Branch 18 → 19 taken 1375 times.
✗ Branch 18 → 27 not taken.
1375x if (op->ec_out)
234 {
235
2/2
✓ Branch 20 → 21 taken 12 times.
✓ Branch 20 → 23 taken 1363 times.
1375x if (op->cancelled.load(std::memory_order_acquire))
236 12x *op->ec_out = capy::error::canceled;
237
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 1362 times.
1363x else if (op->dwError != 0)
238 1x *op->ec_out = make_err(op->dwError);
239 else
240 1362x *op->ec_out = {};
241 }
242
243
4/6
✓ Branch 27 → 28 taken 1362 times.
✓ Branch 27 → 48 taken 13 times.
✓ Branch 28 → 29 taken 1362 times.
✗ Branch 28 → 48 not taken.
✓ Branch 29 → 30 taken 1362 times.
✗ Branch 29 → 48 not taken.
1375x if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
244 {
245 1362x ::setsockopt(
246 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
247
1/1
✓ Branch 30 → 31 taken 1362 times.
1362x reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
248
249 1362x op->peer_wrapper->get_internal()->set_socket(op->accepted_socket);
250
251 1362x sockaddr_storage local_storage{};
252 1362x int local_len = sizeof(local_storage);
253 1362x sockaddr_storage remote_storage{};
254 1362x int remote_len = sizeof(remote_storage);
255
256 1362x endpoint local_ep, remote_ep;
257
1/1
✓ Branch 35 → 36 taken 1362 times.
1362x if (::getsockname(
258 op->accepted_socket,
259
2/2
✓ Branch 36 → 37 taken 1361 times.
✓ Branch 36 → 39 taken 1 time.
1362x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
260 1361x local_ep = from_sockaddr(local_storage);
261
1/1
✓ Branch 39 → 40 taken 1362 times.
1362x if (::getpeername(
262 op->accepted_socket,
263
2/2
✓ Branch 40 → 41 taken 1361 times.
✓ Branch 40 → 43 taken 1 time.
1362x reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) == 0)
264 1361x remote_ep = from_sockaddr(remote_storage);
265
266 1362x op->peer_wrapper->get_internal()->set_endpoints(local_ep, remote_ep);
267 1362x op->accepted_socket = INVALID_SOCKET;
268
269
1/2
✓ Branch 45 → 46 taken 1362 times.
✗ Branch 45 → 47 not taken.
1362x if (op->impl_out)
270 1362x *op->impl_out = op->peer_wrapper;
271 1362x }
272 else
273 {
274
1/2
✓ Branch 48 → 49 taken 13 times.
✗ Branch 48 → 51 not taken.
13x if (op->accepted_socket != INVALID_SOCKET)
275 {
276
1/1
✓ Branch 49 → 50 taken 13 times.
13x ::closesocket(op->accepted_socket);
277 13x op->accepted_socket = INVALID_SOCKET;
278 }
279
280
1/2
✓ Branch 51 → 52 taken 13 times.
✗ Branch 51 → 56 not taken.
13x if (op->peer_wrapper)
281 {
282
1/1
✓ Branch 54 → 55 taken 13 times.
13x op->acceptor_ptr->socket_service().destroy(op->peer_wrapper);
283 13x op->peer_wrapper = nullptr;
284 }
285
286
1/2
✓ Branch 56 → 57 taken 13 times.
✗ Branch 56 → 58 not taken.
13x if (op->impl_out)
287 13x *op->impl_out = nullptr;
288 }
289
290 1375x op->cont_op.cont.h = op->h;
291 1375x auto saved_ex = op->ex;
292 1375x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
293
294
2/2
✓ Branch 60 → 61 taken 1375 times.
✓ Branch 61 → 62 taken 1375 times.
1375x dispatch_coro(saved_ex, op->cont_op.cont).resume();
295 1375x }
296
297 // acceptor_wait_op completion handler
298
299 inline void
300 5x acceptor_wait_op::do_complete(
301 void* owner,
302 scheduler_op* base,
303 std::uint32_t /*bytes*/,
304 std::uint32_t /*error*/)
305 {
306 5x auto* op = static_cast<acceptor_wait_op*>(base);
307
308
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 5 times.
5x if (!owner)
309 {
310 op->cleanup_only();
311 op->acceptor_ptr.reset();
312 return;
313 }
314
315 5x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
316
1/1
✓ Branch 8 → 9 taken 5 times.
5x op->invoke_handler();
317 5x }
318
319 // connect_op completion handler
320
321 inline void
322 1373x connect_op::do_complete(
323 void* owner,
324 scheduler_op* base,
325 std::uint32_t /*bytes*/,
326 std::uint32_t /*error*/)
327 {
328 1373x auto* op = static_cast<connect_op*>(base);
329
330
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1373 times.
1373x 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 1363 times.
✓ Branch 6 → 10 taken 10 times.
✓ Branch 8 → 9 taken 1363 times.
✗ Branch 8 → 10 not taken.
1373x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
339
5/6
✓ Branch 11 → 12 taken 1363 times.
✓ Branch 11 → 15 taken 10 times.
✓ Branch 13 → 14 taken 1363 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 1363 times.
✓ Branch 16 → 27 taken 10 times.
1373x if (success && op->internal.is_open())
340 {
341 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
342
1/1
✓ Branch 18 → 19 taken 1363 times.
1363x ::setsockopt(
343 1363x op->internal.native_handle(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
344 nullptr, 0);
345
346 1363x endpoint local_ep;
347 1363x sockaddr_storage local_storage{};
348 1363x int local_len = sizeof(local_storage);
349
1/1
✓ Branch 21 → 22 taken 1363 times.
1363x if (::getsockname(
350 1363x op->internal.native_handle(),
351
1/2
✓ Branch 22 → 23 taken 1363 times.
✗ Branch 22 → 25 not taken.
1363x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
352 1363x local_ep = from_sockaddr(local_storage);
353 1363x op->internal.set_endpoints(local_ep, op->target_endpoint);
354 }
355
356 1373x auto prevent_premature_destruction = std::move(op->internal_ptr);
357
1/1
✓ Branch 29 → 30 taken 1373 times.
1373x op->invoke_handler();
358 1373x }
359
360 // read_op completion handler
361
362 inline void
363 213877x read_op::do_complete(
364 void* owner,
365 scheduler_op* base,
366 std::uint32_t /*bytes*/,
367 std::uint32_t /*error*/)
368 {
369 213877x auto* op = static_cast<read_op*>(base);
370
371
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 213877 times.
213877x if (!owner)
372 {
373 op->cleanup_only();
374 op->internal_ptr.reset();
375 return;
376 }
377
378 213877x auto prevent_premature_destruction = std::move(op->internal_ptr);
379
1/1
✓ Branch 8 → 9 taken 213877 times.
213877x op->invoke_handler();
380 213877x }
381
382 // write_op completion handler
383
384 inline void
385 212160x write_op::do_complete(
386 void* owner,
387 scheduler_op* base,
388 std::uint32_t /*bytes*/,
389 std::uint32_t /*error*/)
390 {
391 212160x auto* op = static_cast<write_op*>(base);
392
393
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 212160 times.
212160x if (!owner)
394 {
395 op->cleanup_only();
396 op->internal_ptr.reset();
397 return;
398 }
399
400 212160x auto prevent_premature_destruction = std::move(op->internal_ptr);
401
1/1
✓ Branch 8 → 9 taken 212160 times.
212160x op->invoke_handler();
402 212160x }
403
404 // wait_op completion handler
405
406 inline void
407 9x wait_op::do_complete(
408 void* owner,
409 scheduler_op* base,
410 std::uint32_t /*bytes*/,
411 std::uint32_t /*error*/)
412 {
413 9x auto* op = static_cast<wait_op*>(base);
414
415
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 9 times.
9x if (!owner)
416 {
417 op->cleanup_only();
418 op->internal_ptr.reset();
419 return;
420 }
421
422 9x auto prevent_premature_destruction = std::move(op->internal_ptr);
423
1/1
✓ Branch 8 → 9 taken 9 times.
9x op->invoke_handler();
424 9x }
425
426 // win_tcp_socket_internal
427
428 4241x inline win_tcp_socket_internal::win_tcp_socket_internal(win_tcp_service& svc) noexcept
429 4241x : svc_(svc)
430 4241x , conn_(*this)
431 4241x , rd_(*this)
432 4241x , wr_(*this)
433 8482x , wt_(*this)
434 {
435 4241x }
436
437 4241x inline win_tcp_socket_internal::~win_tcp_socket_internal()
438 {
439 4241x svc_.unregister_impl(*this);
440 4241x }
441
442 inline SOCKET
443 20811x win_tcp_socket_internal::native_handle() const noexcept
444 {
445 20811x return socket_;
446 }
447
448 inline endpoint
449 24x win_tcp_socket_internal::local_endpoint() const noexcept
450 {
451 24x return local_endpoint_;
452 }
453
454 inline endpoint
455 23x win_tcp_socket_internal::remote_endpoint() const noexcept
456 {
457 23x return remote_endpoint_;
458 }
459
460 inline bool
461 2269x win_tcp_socket_internal::is_open() const noexcept
462 {
463 2269x return socket_ != INVALID_SOCKET;
464 }
465
466 inline void
467 1362x win_tcp_socket_internal::set_socket(SOCKET s) noexcept
468 {
469 1362x socket_ = s;
470 1362x }
471
472 inline void
473 2725x win_tcp_socket_internal::set_endpoints(endpoint local, endpoint remote) noexcept
474 {
475 2725x local_endpoint_ = local;
476 2725x remote_endpoint_ = remote;
477 2725x }
478
479 inline std::coroutine_handle<>
480 1373x 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 1373 times.
1373x conn_.internal_ptr = shared_from_this();
489
490 1373x auto& op = conn_;
491 1373x op.reset();
492 1373x op.h = h;
493 1373x op.ex = d;
494 1373x op.ec_out = ec;
495 1373x op.target_endpoint = ep;
496 1373x op.start(token);
497
498 1373x 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 12 → 13 taken 1372 times.
✓ Branch 12 → 24 taken 1 time.
1373x if (local_endpoint_ == endpoint{})
503 {
504 1372x sockaddr_storage bind_storage{};
505 socklen_t bind_len;
506
2/2
✓ Branch 13 → 14 taken 5 times.
✓ Branch 13 → 15 taken 1367 times.
1372x if (family_ == AF_INET6)
507 {
508 5x sockaddr_in6 sa6{};
509 5x sa6.sin6_family = AF_INET6;
510 5x sa6.sin6_port = 0;
511 5x sa6.sin6_addr = in6addr_any;
512 5x std::memcpy(&bind_storage, &sa6, sizeof(sa6));
513 5x bind_len = sizeof(sa6);
514 }
515 else
516 {
517 1367x sockaddr_in sa4{};
518 1367x sa4.sin_family = AF_INET;
519 1367x sa4.sin_addr.s_addr = INADDR_ANY;
520 1367x sa4.sin_port = 0;
521 1367x std::memcpy(&bind_storage, &sa4, sizeof(sa4));
522 1367x bind_len = sizeof(sa4);
523 }
524
525
1/1
✓ Branch 16 → 17 taken 1372 times.
1372x if (::bind(
526 socket_, reinterpret_cast<sockaddr*>(&bind_storage),
527
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 23 taken 1372 times.
1372x bind_len) == SOCKET_ERROR)
528 {
529 svc_.on_completion(&op, ::WSAGetLastError(), 0);
530 return std::noop_coroutine();
531 }
532 }
533
534 1373x auto connect_ex = svc_.connect_ex();
535
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 30 taken 1373 times.
1373x if (!connect_ex)
536 {
537 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
538 return std::noop_coroutine();
539 }
540
541 1373x sockaddr_storage storage{};
542 1373x socklen_t addrlen = detail::to_sockaddr(ep, family_, storage);
543
544
1/1
✓ Branch 31 → 32 taken 1373 times.
1373x BOOL result = connect_ex(
545 socket_, reinterpret_cast<sockaddr*>(&storage),
546 static_cast<int>(addrlen), nullptr, 0, nullptr, &op);
547
548
1/2
✓ Branch 32 → 33 taken 1373 times.
✗ Branch 32 → 39 not taken.
1373x if (!result)
549 {
550
1/1
✓ Branch 33 → 34 taken 1373 times.
1373x DWORD err = ::WSAGetLastError();
551
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 39 taken 1373 times.
1373x if (err != ERROR_IO_PENDING)
552 {
553 svc_.on_completion(&op, err, 0);
554 return std::noop_coroutine();
555 }
556 }
557
558 1373x svc_.on_pending(&op);
559 1373x return std::noop_coroutine();
560 }
561
562 inline std::coroutine_handle<>
563 213877x 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 213877 times.
213877x rd_.internal_ptr = shared_from_this();
573
574 213877x auto& op = rd_;
575 213877x op.reset();
576 213877x op.is_read_ = true;
577 213877x op.h = h;
578 213877x op.ex = d;
579 213877x op.ec_out = ec;
580 213877x op.bytes_out = bytes_out;
581 213877x op.start(token);
582
583 213877x svc_.work_started();
584
585 // Prepare buffers
586 213877x capy::mutable_buffer bufs[read_op::max_buffers];
587 213877x op.wsabuf_count =
588 213877x static_cast<DWORD>(param.copy_to(bufs, read_op::max_buffers));
589
590 // Handle empty buffer: complete with 0 bytes
591
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 213876 times.
213877x 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 20 → 17 taken 213878 times.
✓ Branch 20 → 21 taken 213876 times.
427754x for (DWORD i = 0; i < op.wsabuf_count; ++i)
599 {
600 213878x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
601 213878x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
602 }
603
604 213876x op.flags = 0;
605
606 427752x int result = ::WSARecv(
607
1/1
✓ Branch 21 → 22 taken 213876 times.
213876x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
608
609
2/2
✓ Branch 22 → 23 taken 3490 times.
✓ Branch 22 → 29 taken 210386 times.
213876x if (result == SOCKET_ERROR)
610 {
611
1/1
✓ Branch 23 → 24 taken 3490 times.
3490x DWORD err = ::WSAGetLastError();
612
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 24 → 29 taken 3487 times.
3490x if (err != WSA_IO_PENDING)
613 {
614 3x svc_.on_completion(&op, err, 0);
615 3x return std::noop_coroutine();
616 }
617 }
618
619 213873x svc_.on_pending(&op);
620
621 // Re-check cancellation after I/O is pending
622
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 213873 times.
213873x if (op.cancelled.load(std::memory_order_acquire))
623 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
624
625 213873x return std::noop_coroutine();
626 }
627
628 inline std::coroutine_handle<>
629 212160x 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 212160 times.
212160x wr_.internal_ptr = shared_from_this();
639
640 212160x auto& op = wr_;
641 212160x op.reset();
642 212160x op.h = h;
643 212160x op.ex = d;
644 212160x op.ec_out = ec;
645 212160x op.bytes_out = bytes_out;
646 212160x op.start(token);
647
648 212160x svc_.work_started();
649
650 // Prepare buffers
651 212160x capy::mutable_buffer bufs[write_op::max_buffers];
652 212160x op.wsabuf_count =
653 212160x 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 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 212159 times.
212160x 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 20 → 17 taken 212161 times.
✓ Branch 20 → 21 taken 212159 times.
424320x for (DWORD i = 0; i < op.wsabuf_count; ++i)
663 {
664 212161x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
665 212161x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
666 }
667
668 424318x int result = ::WSASend(
669
1/1
✓ Branch 21 → 22 taken 212159 times.
212159x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
670
671
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 29 taken 212158 times.
212159x if (result == SOCKET_ERROR)
672 {
673
1/1
✓ Branch 23 → 24 taken 1 time.
1x DWORD err = ::WSAGetLastError();
674
1/2
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 29 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 212158x svc_.on_pending(&op);
682
683 // Re-check cancellation after I/O is pending
684
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 212158 times.
212158x if (op.cancelled.load(std::memory_order_acquire))
685 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
686
687 212158x return std::noop_coroutine();
688 }
689
690 inline std::coroutine_handle<>
691 9x 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 9 times.
9x wt_.internal_ptr = shared_from_this();
699
700 9x auto& op = wt_;
701 9x op.reset();
702 9x op.h = h;
703 9x op.ex = d;
704 9x op.ec_out = ec;
705 9x op.bytes_out = nullptr;
706 9x op.empty_buffer = true; // skip EOF translation in invoke_handler
707 9x op.start(token);
708
709 9x svc_.work_started();
710
711
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 15 taken 7 times.
9x 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
2/2
✓ Branch 15 → 16 taken 3 times.
✓ Branch 15 → 31 taken 4 times.
7x 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 17 → 18 taken 3 times.
✗ Branch 17 → 24 not taken.
3x if (result == SOCKET_ERROR)
732 {
733 3x DWORD err = ::WSAGetLastError();
734
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 24 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
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 3 times.
3x if (op.cancelled.load(std::memory_order_acquire))
745 ::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 4x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
752 4x return std::noop_coroutine();
753 }
754
755 inline void
756 2112x win_tcp_socket_internal::cancel() noexcept
757 {
758
1/2
✓ Branch 2 → 3 taken 2112 times.
✗ Branch 2 → 4 not taken.
2112x if (socket_ != INVALID_SOCKET)
759 {
760 2112x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
761 }
762
763 2112x conn_.request_cancel();
764 2112x rd_.request_cancel();
765 2112x wr_.request_cancel();
766 2112x 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 2112x svc_.scheduler().cancel_wait_if_constructed(&wt_);
772 2112x }
773
774 inline void
775 12627x 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 12627x wt_.request_cancel();
783 12627x svc_.scheduler().cancel_wait_if_constructed(&wt_);
784
785
2/2
✓ Branch 5 → 6 taken 2760 times.
✓ Branch 5 → 9 taken 9867 times.
12627x if (socket_ != INVALID_SOCKET)
786 {
787 2760x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
788 2760x ::closesocket(socket_);
789 2760x socket_ = INVALID_SOCKET;
790 }
791
792 12627x family_ = AF_UNSPEC;
793
794 // Clear cached endpoints
795 12627x local_endpoint_ = endpoint{};
796 12627x remote_endpoint_ = endpoint{};
797 12627x }
798
799 // win_tcp_socket
800
801 4241x inline win_tcp_socket::win_tcp_socket(
802 4241x std::shared_ptr<win_tcp_socket_internal> internal) noexcept
803 4241x : internal_(std::move(internal))
804 {
805 4241x }
806
807 inline void
808 4241x win_tcp_socket::close_internal() noexcept
809 {
810
1/2
✓ Branch 3 → 4 taken 4241 times.
✗ Branch 3 → 7 not taken.
4241x if (internal_)
811 {
812 4241x internal_->close_socket();
813 4241x internal_.reset();
814 }
815 4241x }
816
817 inline std::coroutine_handle<>
818 1373x 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 1373 times.
1373x return internal_->connect(h, d, ep, token, ec);
826 }
827
828 inline std::coroutine_handle<>
829 213877x 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 213877 times.
213877x return internal_->read_some(h, d, buf, token, ec, bytes);
838 }
839
840 inline std::coroutine_handle<>
841 212160x 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 212160 times.
212160x return internal_->write_some(h, d, buf, token, ec, bytes);
850 }
851
852 inline std::coroutine_handle<>
853 9x 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 9 times.
9x return internal_->wait(h, d, w, token, ec);
861 }
862
863 inline std::error_code
864 5x win_tcp_socket::shutdown(tcp_socket::shutdown_type what) noexcept
865 {
866 int how;
867
3/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 2 times.
✗ Branch 2 → 6 not taken.
5x switch (what)
868 {
869 2x case tcp_socket::shutdown_receive:
870 2x how = SD_RECEIVE;
871 2x break;
872 1x case tcp_socket::shutdown_send:
873 1x how = SD_SEND;
874 1x break;
875 2x case tcp_socket::shutdown_both:
876 2x how = SD_BOTH;
877 2x break;
878 default:
879 return make_err(WSAEINVAL);
880 }
881
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 5 times.
5x if (::shutdown(internal_->native_handle(), how) != 0)
882 return make_err(WSAGetLastError());
883 5x return {};
884 }
885
886 inline native_handle_type
887 14765x win_tcp_socket::native_handle() const noexcept
888 {
889 14765x return static_cast<native_handle_type>(internal_->native_handle());
890 }
891
892 inline std::error_code
893 2376x win_tcp_socket::set_option(
894 int level, int optname, void const* data, std::size_t size) noexcept
895 {
896 2376x if (::setsockopt(
897 2376x internal_->native_handle(), level, optname,
898
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 2376 times.
2376x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
899 return make_err(WSAGetLastError());
900 2376x return {};
901 }
902
903 inline std::error_code
904 33x win_tcp_socket::get_option(
905 int level, int optname, void* data, std::size_t* size) const noexcept
906 {
907 33x int len = static_cast<int>(*size);
908 33x if (::getsockopt(
909 33x internal_->native_handle(), level, optname,
910
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 33 times.
33x reinterpret_cast<char*>(data), &len) != 0)
911 return make_err(WSAGetLastError());
912 33x *size = static_cast<std::size_t>(len);
913 33x return {};
914 }
915
916 inline endpoint
917 24x win_tcp_socket::local_endpoint() const noexcept
918 {
919 24x return internal_->local_endpoint();
920 }
921
922 inline endpoint
923 23x win_tcp_socket::remote_endpoint() const noexcept
924 {
925 23x return internal_->remote_endpoint();
926 }
927
928 inline void
929 2112x win_tcp_socket::cancel() noexcept
930 {
931 2112x internal_->cancel();
932 2112x }
933
934 inline win_tcp_socket_internal*
935 11116x win_tcp_socket::get_internal() const noexcept
936 {
937 11116x return internal_.get();
938 }
939
940 // win_tcp_service
941
942 594x inline win_tcp_service::win_tcp_service(capy::execution_context& ctx)
943 1188x : sched_(ctx.use_service<win_scheduler>())
944
2/2
✓ Branch 5 → 6 taken 594 times.
✓ Branch 6 → 7 taken 594 times.
594x , iocp_(sched_.native_handle())
945 {
946
1/1
✓ Branch 12 → 13 taken 594 times.
594x load_extension_functions();
947 594x }
948
949 1188x 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 594 times.
594x 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 594 times.
594x for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
959 w = acceptor_wrapper_list_.pop_front())
960 delete w;
961 1188x }
962
963 inline void
964 594x win_tcp_service::shutdown()
965 {
966 594x 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 594 times.
594x 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 594 times.
594x for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
980 impl = acceptor_list_.pop_front())
981 {
982 impl->close_socket();
983 }
984 594x }
985
986 inline io_object::implementation*
987 4241x win_tcp_service::construct()
988 {
989
1/1
✓ Branch 2 → 3 taken 4241 times.
4241x auto internal = std::make_shared<win_tcp_socket_internal>(*this);
990
991 {
992 4241x std::lock_guard<win_mutex> lock(mutex_);
993 4241x socket_list_.push_back(internal.get());
994 4241x }
995
996
1/1
✓ Branch 7 → 8 taken 4241 times.
4241x auto* wrapper = new win_tcp_socket(std::move(internal));
997
998 {
999 4241x std::lock_guard<win_mutex> lock(mutex_);
1000 4241x socket_wrapper_list_.push_back(wrapper);
1001 4241x }
1002
1003 4241x return wrapper;
1004 4241x }
1005
1006 inline void
1007 4241x win_tcp_service::destroy(io_object::implementation* p)
1008 {
1009
1/2
✓ Branch 2 → 3 taken 4241 times.
✗ Branch 2 → 5 not taken.
4241x if (p)
1010 {
1011 4241x auto& wrapper = static_cast<win_tcp_socket&>(*p);
1012 4241x wrapper.close_internal();
1013 4241x destroy_impl(wrapper);
1014 }
1015 4241x }
1016
1017 inline void
1018 6988x win_tcp_service::close(io_object::handle& h)
1019 {
1020 6988x auto& wrapper = static_cast<win_tcp_socket&>(*h.get());
1021 6988x wrapper.get_internal()->close_socket();
1022 6988x }
1023
1024 inline void
1025 4241x win_tcp_service::destroy_impl(win_tcp_socket& impl)
1026 {
1027 {
1028 4241x std::lock_guard<win_mutex> lock(mutex_);
1029 4241x socket_wrapper_list_.remove(&impl);
1030 4241x }
1031
1/2
✓ Branch 5 → 6 taken 4241 times.
✗ Branch 5 → 7 not taken.
4241x delete &impl;
1032 4241x }
1033
1034 inline void
1035 4241x win_tcp_service::unregister_impl(win_tcp_socket_internal& impl)
1036 {
1037 4241x std::lock_guard<win_mutex> lock(mutex_);
1038 4241x socket_list_.remove(&impl);
1039 4241x }
1040
1041 inline std::error_code
1042 1398x win_tcp_service::open_socket(
1043 win_tcp_socket_internal& impl, int family, int type, int protocol)
1044 {
1045 1398x impl.close_socket();
1046
1047 SOCKET sock =
1048 1398x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1049
1050
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1398 times.
1398x if (sock == INVALID_SOCKET)
1051 return make_err(::WSAGetLastError());
1052
1053
2/2
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 10 taken 1391 times.
1398x if (family == AF_INET6)
1054 {
1055 7x DWORD one = 1;
1056
1/1
✓ Branch 8 → 9 taken 7 times.
7x ::setsockopt(
1057 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
1058 sizeof(one));
1059 }
1060
1061 2796x HANDLE result = ::CreateIoCompletionPort(
1062 1398x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1063
1064
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1398 times.
1398x if (result == nullptr)
1065 {
1066 DWORD dwError = ::GetLastError();
1067 ::closesocket(sock);
1068 return make_err(dwError);
1069 }
1070
1071 1398x impl.socket_ = sock;
1072 1398x impl.family_ = family;
1073 1398x return {};
1074 }
1075
1076 inline std::error_code
1077 6x win_tcp_service::bind_socket(win_tcp_socket_internal& impl, endpoint ep)
1078 {
1079 6x SOCKET sock = impl.socket_;
1080
1081 6x sockaddr_storage storage{};
1082 6x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1083
1/1
✓ Branch 3 → 4 taken 6 times.
6x if (::bind(
1084 sock, reinterpret_cast<sockaddr*>(&storage),
1085
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 4 times.
6x 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 4x sockaddr_storage local_storage{};
1090 4x int local_len = sizeof(local_storage);
1091
1/1
✓ Branch 7 → 8 taken 4 times.
4x if (::getsockname(
1092
1/2
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 11 not taken.
4x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1093 4x impl.local_endpoint_ = detail::from_sockaddr(local_storage);
1094
1095 4x return {};
1096 }
1097
1098 inline void*
1099 1375x win_tcp_service::native_handle() const noexcept
1100 {
1101 1375x 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 428782x win_tcp_service::on_pending(overlapped_op* op) noexcept
1112 {
1113 428782x sched_.on_pending(op);
1114 428782x }
1115
1116 inline void
1117 9x win_tcp_service::on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept
1118 {
1119 9x sched_.on_completion(op, error, bytes);
1120 9x }
1121
1122 inline void
1123 428799x win_tcp_service::work_started() noexcept
1124 {
1125 428799x sched_.work_started();
1126 428799x }
1127
1128 inline void
1129 win_tcp_service::work_finished() noexcept
1130 {
1131 sched_.work_finished();
1132 }
1133
1134 inline void
1135 594x win_tcp_service::load_extension_functions()
1136 {
1137
1/1
✓ Branch 2 → 3 taken 594 times.
594x 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 594 times.
594x if (sock == INVALID_SOCKET)
1141 return;
1142
1143 594x DWORD bytes = 0;
1144
1145 594x GUID connect_ex_guid = WSAID_CONNECTEX;
1146 594x ::WSAIoctl(
1147 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
1148
1/1
✓ Branch 5 → 6 taken 594 times.
594x sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
1149 nullptr, nullptr);
1150
1151 594x GUID accept_ex_guid = WSAID_ACCEPTEX;
1152 594x ::WSAIoctl(
1153 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
1154
1/1
✓ Branch 6 → 7 taken 594 times.
594x sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
1155 nullptr, nullptr);
1156
1157
1/1
✓ Branch 7 → 8 taken 594 times.
594x ::closesocket(sock);
1158 }
1159
1160 inline void
1161 1340x win_tcp_service::destroy_acceptor_impl(win_tcp_acceptor& impl)
1162 {
1163 {
1164 1340x std::lock_guard<win_mutex> lock(mutex_);
1165 1340x acceptor_wrapper_list_.remove(&impl);
1166 1340x }
1167
1/2
✓ Branch 5 → 6 taken 1340 times.
✗ Branch 5 → 7 not taken.
1340x delete &impl;
1168 1340x }
1169
1170 inline void
1171 1340x win_tcp_service::unregister_acceptor_impl(win_tcp_acceptor_internal& impl)
1172 {
1173 1340x std::lock_guard<win_mutex> lock(mutex_);
1174 1340x acceptor_list_.remove(&impl);
1175 1340x }
1176
1177 inline std::error_code
1178 1335x win_tcp_service::open_acceptor_socket(
1179 win_tcp_acceptor_internal& impl, int family, int type, int protocol)
1180 {
1181 1335x impl.close_socket();
1182
1183 SOCKET sock =
1184 1335x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1185
1186
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1335 times.
1335x if (sock == INVALID_SOCKET)
1187 return make_err(::WSAGetLastError());
1188
1189
2/2
✓ Branch 7 → 8 taken 9 times.
✓ Branch 7 → 10 taken 1326 times.
1335x if (family == AF_INET6)
1190 {
1191 9x DWORD val = 0; // dual-stack default
1192
1/1
✓ Branch 8 → 9 taken 9 times.
9x ::setsockopt(
1193 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&val),
1194 sizeof(val));
1195 }
1196
1197 2670x HANDLE result = ::CreateIoCompletionPort(
1198 1335x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1199
1200
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1335 times.
1335x if (result == nullptr)
1201 {
1202 DWORD dwError = ::GetLastError();
1203 ::closesocket(sock);
1204 return make_err(dwError);
1205 }
1206
1207 1335x impl.socket_ = sock;
1208 1335x return {};
1209 }
1210
1211 inline std::error_code
1212 1334x win_tcp_service::bind_acceptor(win_tcp_acceptor_internal& impl, endpoint ep)
1213 {
1214 1334x SOCKET sock = impl.socket_;
1215
1216 1334x sockaddr_storage storage{};
1217 1334x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1218
1/1
✓ Branch 3 → 4 taken 1334 times.
1334x if (::bind(
1219 sock, reinterpret_cast<sockaddr*>(&storage),
1220
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 7 taken 1330 times.
1334x 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 1330x sockaddr_storage local_storage{};
1225 1330x int local_len = sizeof(local_storage);
1226
1/1
✓ Branch 7 → 8 taken 1330 times.
1330x if (::getsockname(
1227
1/2
✓ Branch 8 → 9 taken 1330 times.
✗ Branch 8 → 11 not taken.
1330x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1228 1330x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1229
1230 1330x return {};
1231 }
1232
1233 inline std::error_code
1234 1323x win_tcp_service::listen_acceptor(win_tcp_acceptor_internal& impl, int backlog)
1235 {
1236 1323x SOCKET sock = impl.socket_;
1237
1238
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1323 times.
1323x if (::listen(sock, backlog) == SOCKET_ERROR)
1239 return make_err(::WSAGetLastError());
1240
1241 1323x return {};
1242 }
1243
1244 // win_tcp_acceptor_internal
1245
1246 1340x inline win_tcp_acceptor_internal::win_tcp_acceptor_internal(win_tcp_service& svc) noexcept
1247 1340x : svc_(svc)
1248 {
1249 1340x }
1250
1251 1340x inline win_tcp_acceptor_internal::~win_tcp_acceptor_internal()
1252 {
1253 1340x svc_.unregister_acceptor_impl(*this);
1254 1340x }
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 1332x win_tcp_acceptor_internal::native_handle() const noexcept
1264 {
1265 1332x return socket_;
1266 }
1267
1268 inline endpoint
1269 1318x win_tcp_acceptor_internal::local_endpoint() const noexcept
1270 {
1271 1318x return local_endpoint_;
1272 }
1273
1274 inline bool
1275 10693x win_tcp_acceptor_internal::is_open() const noexcept
1276 {
1277 10693x return socket_ != INVALID_SOCKET;
1278 }
1279
1280 inline void
1281 1330x win_tcp_acceptor_internal::set_local_endpoint(endpoint ep) noexcept
1282 {
1283 1330x local_endpoint_ = ep;
1284 1330x }
1285
1286 inline std::coroutine_handle<>
1287 1375x 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 1375 times.
1375x acc_.acceptor_ptr = shared_from_this();
1296
1297 1375x auto& op = acc_;
1298 1375x op.reset();
1299 1375x op.h = h;
1300 1375x op.ex = d;
1301 1375x op.ec_out = ec;
1302 1375x op.impl_out = impl_out;
1303 1375x op.start(token);
1304
1305 1375x svc_.work_started();
1306
1307 // Create wrapper for the peer socket (service owns it)
1308
1/1
✓ Branch 10 → 11 taken 1375 times.
1375x 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 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 1369 times.
1375x int af = local_endpoint_.is_v6() ? AF_INET6 : AF_INET;
1312
1313 // Create the accepted socket with matching address family
1314
1/1
✓ Branch 15 → 16 taken 1375 times.
1375x SOCKET accepted = ::WSASocketW(
1315 af, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1316
1317
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 23 taken 1375 times.
1375x 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 24 → 25 taken 1375 times.
1375x HANDLE result = ::CreateIoCompletionPort(
1325 1375x reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
1326
1327
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 33 taken 1375 times.
1375x 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 1375x op.accepted_socket = accepted;
1338 1375x op.peer_wrapper = &peer_wrapper;
1339 1375x op.listen_socket = socket_;
1340
1341 1375x auto accept_ex = svc_.accept_ex();
1342
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 41 taken 1375 times.
1375x 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 41 → 42 taken 6 times.
✓ Branch 41 → 43 taken 1369 times.
1375x DWORD addr_size = static_cast<DWORD>(
1354 (af == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) + 16);
1355 1375x DWORD bytes_received = 0;
1356
1357 2750x BOOL ok = accept_ex(
1358
1/1
✓ Branch 44 → 45 taken 1375 times.
1375x socket_, accepted, op.addr_buf, 0, addr_size, addr_size,
1359 &bytes_received, &op);
1360
1361
1/2
✓ Branch 45 → 46 taken 1375 times.
✗ Branch 45 → 54 not taken.
1375x if (!ok)
1362 {
1363
1/1
✓ Branch 46 → 47 taken 1375 times.
1375x DWORD err = ::WSAGetLastError();
1364
1/2
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 54 taken 1375 times.
1375x 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 1375x 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 56 → 57 taken 1 time.
✓ Branch 56 → 58 taken 1374 times.
1375x if (op.cancelled.load(std::memory_order_acquire))
1382
1/1
✓ Branch 57 → 58 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
1383
1384 1375x return std::noop_coroutine();
1385 }
1386
1387 inline std::coroutine_handle<>
1388 5x 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 5 times.
5x wt_.acceptor_ptr = shared_from_this();
1396 5x wt_.listen_socket = socket_;
1397
1398 5x auto& op = wt_;
1399 5x op.reset();
1400 5x op.h = h;
1401 5x op.ex = d;
1402 5x op.ec_out = ec;
1403 5x op.bytes_out = nullptr;
1404 5x op.start(token);
1405
1406 5x svc_.work_started();
1407
1408
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 15 taken 4 times.
5x if (w == wait_type::write)
1409 {
1410 1x svc_.on_completion(&op, 0, 0);
1411 1x 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 4x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
1417 4x return std::noop_coroutine();
1418 }
1419
1420 inline void
1421 4x win_tcp_acceptor_internal::cancel() noexcept
1422 {
1423
1/2
✓ Branch 2 → 3 taken 4 times.
✗ Branch 2 → 4 not taken.
4x if (socket_ != INVALID_SOCKET)
1424 {
1425 4x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1426 }
1427
1428 4x acc_.request_cancel();
1429 4x wt_.request_cancel();
1430 4x svc_.scheduler().cancel_wait_if_constructed(&wt_);
1431 4x }
1432
1433 inline void
1434 5350x win_tcp_acceptor_internal::close_socket() noexcept
1435 {
1436 // Tear down any aux-reactor-parked wait op first.
1437 5350x wt_.request_cancel();
1438 5350x svc_.scheduler().cancel_wait_if_constructed(&wt_);
1439
1440
2/2
✓ Branch 5 → 6 taken 1335 times.
✓ Branch 5 → 9 taken 4015 times.
5350x if (socket_ != INVALID_SOCKET)
1441 {
1442 1335x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1443 1335x ::closesocket(socket_);
1444 1335x socket_ = INVALID_SOCKET;
1445 }
1446
1447 // Clear cached endpoint
1448 5350x local_endpoint_ = endpoint{};
1449 5350x }
1450
1451 // win_tcp_acceptor
1452
1453 1340x inline win_tcp_acceptor::win_tcp_acceptor(
1454 1340x std::shared_ptr<win_tcp_acceptor_internal> internal) noexcept
1455 1340x : internal_(std::move(internal))
1456 {
1457 1340x }
1458
1459 inline void
1460 1340x win_tcp_acceptor::close_internal() noexcept
1461 {
1462
1/2
✓ Branch 3 → 4 taken 1340 times.
✗ Branch 3 → 7 not taken.
1340x if (internal_)
1463 {
1464 1340x internal_->close_socket();
1465 1340x internal_.reset();
1466 }
1467 1340x }
1468
1469 inline std::coroutine_handle<>
1470 1375x 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 1375 times.
1375x return internal_->accept(h, d, token, ec, impl_out);
1478 }
1479
1480 inline std::coroutine_handle<>
1481 5x 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 5 times.
5x return internal_->wait(h, d, w, token, ec);
1489 }
1490
1491 inline endpoint
1492 1318x win_tcp_acceptor::local_endpoint() const noexcept
1493 {
1494 1318x return internal_->local_endpoint();
1495 }
1496
1497 inline bool
1498 10693x win_tcp_acceptor::is_open() const noexcept
1499 {
1500
3/4
✓ Branch 3 → 4 taken 10693 times.
✗ Branch 3 → 8 not taken.
✓ Branch 6 → 7 taken 8044 times.
✓ Branch 6 → 8 taken 2649 times.
10693x return internal_ && internal_->is_open();
1501 }
1502
1503 inline void
1504 4x win_tcp_acceptor::cancel() noexcept
1505 {
1506 4x internal_->cancel();
1507 4x }
1508
1509 inline std::error_code
1510 1332x win_tcp_acceptor::set_option(
1511 int level, int optname, void const* data, std::size_t size) noexcept
1512 {
1513 1332x if (::setsockopt(
1514 1332x internal_->native_handle(), level, optname,
1515
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1332 times.
1332x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
1516 return make_err(WSAGetLastError());
1517 1332x 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 6667x win_tcp_acceptor::get_internal() const noexcept
1535 {
1536 6667x return internal_.get();
1537 }
1538
1539 // win_tcp_acceptor_service
1540
1541 594x inline win_tcp_acceptor_service::win_tcp_acceptor_service(
1542 594x capy::execution_context& ctx, win_tcp_service& svc)
1543 594x : svc_(svc)
1544 {
1545 (void)ctx;
1546 594x }
1547
1548 inline io_object::implementation*
1549 1340x win_tcp_acceptor_service::construct()
1550 {
1551
1/1
✓ Branch 2 → 3 taken 1340 times.
1340x auto internal = std::make_shared<win_tcp_acceptor_internal>(svc_);
1552
1553 {
1554 1340x std::lock_guard<win_mutex> lock(svc_.mutex_);
1555 1340x svc_.acceptor_list_.push_back(internal.get());
1556 1340x }
1557
1558
1/1
✓ Branch 7 → 8 taken 1340 times.
1340x auto* wrapper = new win_tcp_acceptor(std::move(internal));
1559
1560 {
1561 1340x std::lock_guard<win_mutex> lock(svc_.mutex_);
1562 1340x svc_.acceptor_wrapper_list_.push_back(wrapper);
1563 1340x }
1564
1565 1340x return wrapper;
1566 1340x }
1567
1568 inline void
1569 1340x win_tcp_acceptor_service::destroy(io_object::implementation* p)
1570 {
1571
1/2
✓ Branch 2 → 3 taken 1340 times.
✗ Branch 2 → 5 not taken.
1340x if (p)
1572 {
1573 1340x auto& wrapper = static_cast<win_tcp_acceptor&>(*p);
1574 1340x wrapper.close_internal();
1575 1340x svc_.destroy_acceptor_impl(wrapper);
1576 }
1577 1340x }
1578
1579 inline void
1580 2675x win_tcp_acceptor_service::close(io_object::handle& h)
1581 {
1582 2675x auto& wrapper = static_cast<win_tcp_acceptor&>(*h.get());
1583 2675x wrapper.get_internal()->close_socket();
1584 2675x }
1585
1586 inline std::error_code
1587 1335x win_tcp_acceptor_service::open_acceptor_socket(
1588 tcp_acceptor::implementation& impl, int family, int type, int protocol)
1589 {
1590 1335x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1591 1335x return svc_.open_acceptor_socket(
1592 2670x *wrapper.get_internal(), family, type, protocol);
1593 }
1594
1595 inline std::error_code
1596 1334x win_tcp_acceptor_service::bind_acceptor(
1597 tcp_acceptor::implementation& impl, endpoint ep)
1598 {
1599 1334x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1600 1334x return svc_.bind_acceptor(*wrapper.get_internal(), ep);
1601 }
1602
1603 inline std::error_code
1604 1323x win_tcp_acceptor_service::listen_acceptor(
1605 tcp_acceptor::implementation& impl, int backlog)
1606 {
1607 1323x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1608 1323x return svc_.listen_acceptor(*wrapper.get_internal(), backlog);
1609 }
1610
1611 inline void
1612 594x win_tcp_acceptor_service::shutdown()
1613 {
1614 594x }
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