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

84.6% Lines (624/738) 95.8% List of functions (91/95) 64.1% Branches (184/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 4252x 100.0% 100.0% boost::corosio::detail::read_op::read_op(boost::corosio::detail::win_tcp_socket_internal&) :83 4252x 100.0% 100.0% boost::corosio::detail::write_op::write_op(boost::corosio::detail::win_tcp_socket_internal&) :90 4252x 100.0% 100.0% boost::corosio::detail::wait_op::wait_op(boost::corosio::detail::win_tcp_socket_internal&) :97 4252x 100.0% 100.0% boost::corosio::detail::accept_op::accept_op() :104 1341x 100.0% 100.0% boost::corosio::detail::acceptor_wait_op::acceptor_wait_op() :109 1341x 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 904x 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 0 0.0% 0.0% 0.0% boost::corosio::detail::accept_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :173 10x 100.0% 50.0% 83.3% boost::corosio::detail::acceptor_wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :183 0 0.0% 0.0% 0.0% boost::corosio::detail::accept_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :201 1379x 83.0% 66.7% 80.9% boost::corosio::detail::acceptor_wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :300 2x 66.7% 66.7% 61.5% boost::corosio::detail::connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :322 1376x 85.7% 76.5% 82.9% boost::corosio::detail::read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :363 212546x 66.7% 66.7% 61.5% boost::corosio::detail::write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :385 210886x 66.7% 66.7% 61.5% boost::corosio::detail::wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :407 4x 66.7% 66.7% 61.5% boost::corosio::detail::win_tcp_socket_internal::win_tcp_socket_internal(boost::corosio::detail::win_tcp_service&) :428 4252x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::~win_tcp_socket_internal() :437 4252x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::native_handle() const :443 20968x 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 2275x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_socket(unsigned long long) :467 1367x 100.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::set_endpoints(boost::corosio::endpoint, boost::corosio::endpoint) :473 2735x 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 1376x 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 212546x 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 210886x 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 4x 82.8% 63.6% 70.3% boost::corosio::detail::win_tcp_socket_internal::cancel() :756 2142x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket_internal::close_socket() :775 12661x 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 4252x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::close_internal() :808 4252x 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 1376x 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 212546x 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 210886x 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 4x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_socket::shutdown(boost::corosio::shutdown_type) :864 3x 81.2% 66.7% 76.9% boost::corosio::detail::win_tcp_socket::native_handle() const :887 14879x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::set_option(int, int, void const*, unsigned long long) :893 2410x 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 2142x 100.0% 100.0% boost::corosio::detail::win_tcp_socket::get_internal() const :935 11149x 100.0% 100.0% boost::corosio::detail::win_tcp_service::win_tcp_service(boost::capy::execution_context&) :942 567x 100.0% 100.0% 65.0% boost::corosio::detail::win_tcp_service::~win_tcp_service() :949 1134x 100.0% 100.0% boost::corosio::detail::win_tcp_service::shutdown() :964 567x 55.6% 50.0% 63.6% boost::corosio::detail::win_tcp_service::construct() :987 4252x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_service::destroy(boost::corosio::io_object::implementation*) :1007 4252x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::close(boost::corosio::io_object::handle&) :1018 7008x 100.0% 100.0% boost::corosio::detail::win_tcp_service::destroy_impl(boost::corosio::detail::win_tcp_socket&) :1025 4252x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_impl(boost::corosio::detail::win_tcp_socket_internal&) :1035 4252x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_socket(boost::corosio::detail::win_tcp_socket_internal&, int, int, int) :1042 1401x 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 1379x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_pending(boost::corosio::detail::overlapped_op*) :1111 426183x 100.0% 100.0% boost::corosio::detail::win_tcp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1117 8x 100.0% 100.0% boost::corosio::detail::win_tcp_service::work_started() :1123 426193x 100.0% 100.0% boost::corosio::detail::win_tcp_service::load_extension_functions() :1135 567x 91.7% 83.3% 88.9% boost::corosio::detail::win_tcp_service::destroy_acceptor_impl(boost::corosio::detail::win_tcp_acceptor&) :1161 1341x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_service::unregister_acceptor_impl(boost::corosio::detail::win_tcp_acceptor_internal&) :1171 1341x 100.0% 100.0% boost::corosio::detail::win_tcp_service::open_acceptor_socket(boost::corosio::detail::win_tcp_acceptor_internal&, int, int, int) :1178 1336x 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 1335x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::listen_acceptor(boost::corosio::detail::win_tcp_acceptor_internal&, int) :1234 1324x 80.0% 50.0% 66.7% boost::corosio::detail::win_tcp_acceptor_internal::win_tcp_acceptor_internal(boost::corosio::detail::win_tcp_service&) :1246 1341x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::~win_tcp_acceptor_internal() :1251 1341x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::socket_service() :1257 12x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::native_handle() const :1263 1333x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::local_endpoint() const :1269 1323x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::is_open() const :1275 10711x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::set_local_endpoint(boost::corosio::endpoint) :1281 1331x 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 1379x 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 2x 87.5% 66.7% 81.0% boost::corosio::detail::win_tcp_acceptor_internal::cancel() :1421 3x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_internal::close_socket() :1434 5354x 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 1341x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::close_internal() :1460 1341x 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 1379x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :1481 2x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_acceptor::local_endpoint() const :1492 1323x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::is_open() const :1498 10711x 100.0% 75.0% 100.0% boost::corosio::detail::win_tcp_acceptor::cancel() :1504 3x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::set_option(int, int, void const*, unsigned long long) :1510 1333x 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 6672x 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 567x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::construct() :1549 1341x 100.0% 100.0% 89.5% boost::corosio::detail::win_tcp_acceptor_service::destroy(boost::corosio::io_object::implementation*) :1569 1341x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::close(boost::corosio::io_object::handle&) :1580 2677x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::open_acceptor_socket(boost::corosio::tcp_acceptor::implementation&, int, int, int) :1587 1336x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::bind_acceptor(boost::corosio::tcp_acceptor::implementation&, boost::corosio::endpoint) :1596 1335x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::listen_acceptor(boost::corosio::tcp_acceptor::implementation&, int) :1604 1324x 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor_service::shutdown() :1612 567x 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 4252x inline connect_op::connect_op(win_tcp_socket_internal& internal_) noexcept
77 : overlapped_op(&do_complete)
78 4252x , internal(internal_)
79 {
80 4252x cancel_func_ = &do_cancel_impl;
81 4252x }
82
83 4252x inline read_op::read_op(win_tcp_socket_internal& internal_) noexcept
84 : overlapped_op(&do_complete)
85 4252x , internal(internal_)
86 {
87 4252x cancel_func_ = &do_cancel_impl;
88 4252x }
89
90 4252x inline write_op::write_op(win_tcp_socket_internal& internal_) noexcept
91 : overlapped_op(&do_complete)
92 4252x , internal(internal_)
93 {
94 4252x cancel_func_ = &do_cancel_impl;
95 4252x }
96
97 4252x inline wait_op::wait_op(win_tcp_socket_internal& internal_) noexcept
98 : overlapped_op(&do_complete)
99 4252x , internal(internal_)
100 {
101 4252x cancel_func_ = &do_cancel_impl;
102 4252x }
103
104 1341x inline accept_op::accept_op() noexcept : overlapped_op(&do_complete)
105 {
106 1341x cancel_func_ = &do_cancel_impl;
107 1341x }
108
109 1341x inline acceptor_wait_op::acceptor_wait_op() noexcept
110 1341x : overlapped_op(&do_complete)
111 {
112 1341x cancel_func_ = &do_cancel_impl;
113 1341x }
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 904x read_op::do_cancel_impl(overlapped_op* base) noexcept
130 {
131 904x auto* op = static_cast<read_op*>(base);
132 904x op->cancelled.store(true, std::memory_order_release);
133
1/2
✓ Branch 4 → 5 taken 904 times.
✗ Branch 4 → 10 not taken.
904x if (op->internal.is_open())
134 {
135
1/2
✓ Branch 5 → 6 taken 904 times.
✗ Branch 5 → 7 not taken.
1808x ::CancelIoEx(
136 904x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
137 }
138 904x }
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 wait_op::do_cancel_impl(overlapped_op* base) noexcept
154 {
155 auto* op = static_cast<wait_op*>(base);
156 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 if (op->internal.is_open())
161 {
162 ::CancelIoEx(
163 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 op->internal.svc_.scheduler().cancel_wait_if_constructed(op);
170 }
171
172 inline void
173 10x accept_op::do_cancel_impl(overlapped_op* base) noexcept
174 {
175 10x auto* op = static_cast<accept_op*>(base);
176
1/2
✓ Branch 2 → 3 taken 10 times.
✗ Branch 2 → 7 not taken.
10x if (op->listen_socket != INVALID_SOCKET)
177 {
178
1/2
✓ Branch 3 → 4 taken 10 times.
✗ Branch 3 → 5 not taken.
10x ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
179 }
180 10x }
181
182 inline void
183 acceptor_wait_op::do_cancel_impl(overlapped_op* base) noexcept
184 {
185 auto* op = static_cast<acceptor_wait_op*>(base);
186 op->cancelled.store(true, std::memory_order_release);
187 if (op->listen_socket != INVALID_SOCKET)
188 {
189 ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
190 }
191 if (op->acceptor_ptr)
192 {
193 op->acceptor_ptr->socket_service().scheduler()
194 .cancel_wait_if_constructed(op);
195 }
196 }
197
198 // accept_op completion handler
199
200 inline void
201 1379x accept_op::do_complete(
202 void* owner,
203 scheduler_op* base,
204 std::uint32_t /*bytes*/,
205 std::uint32_t /*error*/)
206 {
207 1379x auto* op = static_cast<accept_op*>(base);
208
209
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 1379 times.
1379x 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 1379x op->stop_cb.reset();
229
230 bool success =
231
3/4
✓ Branch 13 → 14 taken 1367 times.
✓ Branch 13 → 17 taken 12 times.
✓ Branch 15 → 16 taken 1367 times.
✗ Branch 15 → 17 not taken.
1379x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
232
233
1/2
✓ Branch 18 → 19 taken 1379 times.
✗ Branch 18 → 27 not taken.
1379x if (op->ec_out)
234 {
235
2/2
✓ Branch 20 → 21 taken 11 times.
✓ Branch 20 → 23 taken 1368 times.
1379x if (op->cancelled.load(std::memory_order_acquire))
236 11x *op->ec_out = capy::error::canceled;
237
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 1367 times.
1368x else if (op->dwError != 0)
238 1x *op->ec_out = make_err(op->dwError);
239 else
240 1367x *op->ec_out = {};
241 }
242
243
4/6
✓ Branch 27 → 28 taken 1367 times.
✓ Branch 27 → 48 taken 12 times.
✓ Branch 28 → 29 taken 1367 times.
✗ Branch 28 → 48 not taken.
✓ Branch 29 → 30 taken 1367 times.
✗ Branch 29 → 48 not taken.
1379x if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
244 {
245 1367x ::setsockopt(
246 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
247
1/1
✓ Branch 30 → 31 taken 1367 times.
1367x reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
248
249 1367x op->peer_wrapper->get_internal()->set_socket(op->accepted_socket);
250
251 1367x sockaddr_storage local_storage{};
252 1367x int local_len = sizeof(local_storage);
253 1367x sockaddr_storage remote_storage{};
254 1367x int remote_len = sizeof(remote_storage);
255
256 1367x endpoint local_ep, remote_ep;
257
1/1
✓ Branch 35 → 36 taken 1367 times.
1367x if (::getsockname(
258 op->accepted_socket,
259
2/2
✓ Branch 36 → 37 taken 1366 times.
✓ Branch 36 → 39 taken 1 time.
1367x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
260 1366x local_ep = from_sockaddr(local_storage);
261
1/1
✓ Branch 39 → 40 taken 1367 times.
1367x if (::getpeername(
262 op->accepted_socket,
263
2/2
✓ Branch 40 → 41 taken 1366 times.
✓ Branch 40 → 43 taken 1 time.
1367x reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) == 0)
264 1366x remote_ep = from_sockaddr(remote_storage);
265
266 1367x op->peer_wrapper->get_internal()->set_endpoints(local_ep, remote_ep);
267 1367x op->accepted_socket = INVALID_SOCKET;
268
269
1/2
✓ Branch 45 → 46 taken 1367 times.
✗ Branch 45 → 47 not taken.
1367x if (op->impl_out)
270 1367x *op->impl_out = op->peer_wrapper;
271 1367x }
272 else
273 {
274
1/2
✓ Branch 48 → 49 taken 12 times.
✗ Branch 48 → 51 not taken.
12x if (op->accepted_socket != INVALID_SOCKET)
275 {
276
1/1
✓ Branch 49 → 50 taken 12 times.
12x ::closesocket(op->accepted_socket);
277 12x op->accepted_socket = INVALID_SOCKET;
278 }
279
280
1/2
✓ Branch 51 → 52 taken 12 times.
✗ Branch 51 → 56 not taken.
12x if (op->peer_wrapper)
281 {
282
1/1
✓ Branch 54 → 55 taken 12 times.
12x op->acceptor_ptr->socket_service().destroy(op->peer_wrapper);
283 12x op->peer_wrapper = nullptr;
284 }
285
286
1/2
✓ Branch 56 → 57 taken 12 times.
✗ Branch 56 → 58 not taken.
12x if (op->impl_out)
287 12x *op->impl_out = nullptr;
288 }
289
290 1379x op->cont_op.cont.h = op->h;
291 1379x auto saved_ex = op->ex;
292 1379x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
293
294
2/2
✓ Branch 60 → 61 taken 1379 times.
✓ Branch 61 → 62 taken 1379 times.
1379x dispatch_coro(saved_ex, op->cont_op.cont).resume();
295 1379x }
296
297 // acceptor_wait_op completion handler
298
299 inline void
300 2x acceptor_wait_op::do_complete(
301 void* owner,
302 scheduler_op* base,
303 std::uint32_t /*bytes*/,
304 std::uint32_t /*error*/)
305 {
306 2x auto* op = static_cast<acceptor_wait_op*>(base);
307
308
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 2 times.
2x if (!owner)
309 {
310 op->cleanup_only();
311 op->acceptor_ptr.reset();
312 return;
313 }
314
315 2x auto prevent_premature_destruction = std::move(op->acceptor_ptr);
316
1/1
✓ Branch 8 → 9 taken 2 times.
2x op->invoke_handler();
317 2x }
318
319 // connect_op completion handler
320
321 inline void
322 1376x connect_op::do_complete(
323 void* owner,
324 scheduler_op* base,
325 std::uint32_t /*bytes*/,
326 std::uint32_t /*error*/)
327 {
328 1376x auto* op = static_cast<connect_op*>(base);
329
330
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1376 times.
1376x 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 1368 times.
✓ Branch 6 → 10 taken 8 times.
✓ Branch 8 → 9 taken 1368 times.
✗ Branch 8 → 10 not taken.
1376x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
339
5/6
✓ Branch 11 → 12 taken 1368 times.
✓ Branch 11 → 15 taken 8 times.
✓ Branch 13 → 14 taken 1368 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 1368 times.
✓ Branch 16 → 27 taken 8 times.
1376x if (success && op->internal.is_open())
340 {
341 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
342
1/1
✓ Branch 18 → 19 taken 1368 times.
1368x ::setsockopt(
343 1368x op->internal.native_handle(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
344 nullptr, 0);
345
346 1368x endpoint local_ep;
347 1368x sockaddr_storage local_storage{};
348 1368x int local_len = sizeof(local_storage);
349
1/1
✓ Branch 21 → 22 taken 1368 times.
1368x if (::getsockname(
350 1368x op->internal.native_handle(),
351
1/2
✓ Branch 22 → 23 taken 1368 times.
✗ Branch 22 → 25 not taken.
1368x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
352 1368x local_ep = from_sockaddr(local_storage);
353 1368x op->internal.set_endpoints(local_ep, op->target_endpoint);
354 }
355
356 1376x auto prevent_premature_destruction = std::move(op->internal_ptr);
357
1/1
✓ Branch 29 → 30 taken 1376 times.
1376x op->invoke_handler();
358 1376x }
359
360 // read_op completion handler
361
362 inline void
363 212546x read_op::do_complete(
364 void* owner,
365 scheduler_op* base,
366 std::uint32_t /*bytes*/,
367 std::uint32_t /*error*/)
368 {
369 212546x auto* op = static_cast<read_op*>(base);
370
371
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 212546 times.
212546x if (!owner)
372 {
373 op->cleanup_only();
374 op->internal_ptr.reset();
375 return;
376 }
377
378 212546x auto prevent_premature_destruction = std::move(op->internal_ptr);
379
1/1
✓ Branch 8 → 9 taken 212546 times.
212546x op->invoke_handler();
380 212546x }
381
382 // write_op completion handler
383
384 inline void
385 210886x write_op::do_complete(
386 void* owner,
387 scheduler_op* base,
388 std::uint32_t /*bytes*/,
389 std::uint32_t /*error*/)
390 {
391 210886x auto* op = static_cast<write_op*>(base);
392
393
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 210886 times.
210886x if (!owner)
394 {
395 op->cleanup_only();
396 op->internal_ptr.reset();
397 return;
398 }
399
400 210886x auto prevent_premature_destruction = std::move(op->internal_ptr);
401
1/1
✓ Branch 8 → 9 taken 210886 times.
210886x op->invoke_handler();
402 210886x }
403
404 // wait_op completion handler
405
406 inline void
407 4x wait_op::do_complete(
408 void* owner,
409 scheduler_op* base,
410 std::uint32_t /*bytes*/,
411 std::uint32_t /*error*/)
412 {
413 4x auto* op = static_cast<wait_op*>(base);
414
415
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 4 times.
4x if (!owner)
416 {
417 op->cleanup_only();
418 op->internal_ptr.reset();
419 return;
420 }
421
422 4x auto prevent_premature_destruction = std::move(op->internal_ptr);
423
1/1
✓ Branch 8 → 9 taken 4 times.
4x op->invoke_handler();
424 4x }
425
426 // win_tcp_socket_internal
427
428 4252x inline win_tcp_socket_internal::win_tcp_socket_internal(win_tcp_service& svc) noexcept
429 4252x : svc_(svc)
430 4252x , conn_(*this)
431 4252x , rd_(*this)
432 4252x , wr_(*this)
433 8504x , wt_(*this)
434 {
435 4252x }
436
437 4252x inline win_tcp_socket_internal::~win_tcp_socket_internal()
438 {
439 4252x svc_.unregister_impl(*this);
440 4252x }
441
442 inline SOCKET
443 20968x win_tcp_socket_internal::native_handle() const noexcept
444 {
445 20968x 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 2275x win_tcp_socket_internal::is_open() const noexcept
462 {
463 2275x return socket_ != INVALID_SOCKET;
464 }
465
466 inline void
467 1367x win_tcp_socket_internal::set_socket(SOCKET s) noexcept
468 {
469 1367x socket_ = s;
470 1367x }
471
472 inline void
473 2735x win_tcp_socket_internal::set_endpoints(endpoint local, endpoint remote) noexcept
474 {
475 2735x local_endpoint_ = local;
476 2735x remote_endpoint_ = remote;
477 2735x }
478
479 inline std::coroutine_handle<>
480 1376x 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 1376 times.
1376x conn_.internal_ptr = shared_from_this();
489
490 1376x auto& op = conn_;
491 1376x op.reset();
492 1376x op.h = h;
493 1376x op.ex = d;
494 1376x op.ec_out = ec;
495 1376x op.target_endpoint = ep;
496 1376x op.start(token);
497
498 1376x 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 1375 times.
✓ Branch 12 → 24 taken 1 time.
1376x if (local_endpoint_ == endpoint{})
503 {
504 1375x sockaddr_storage bind_storage{};
505 socklen_t bind_len;
506
2/2
✓ Branch 13 → 14 taken 5 times.
✓ Branch 13 → 15 taken 1370 times.
1375x 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 1370x sockaddr_in sa4{};
518 1370x sa4.sin_family = AF_INET;
519 1370x sa4.sin_addr.s_addr = INADDR_ANY;
520 1370x sa4.sin_port = 0;
521 1370x std::memcpy(&bind_storage, &sa4, sizeof(sa4));
522 1370x bind_len = sizeof(sa4);
523 }
524
525
1/1
✓ Branch 16 → 17 taken 1375 times.
1375x if (::bind(
526 socket_, reinterpret_cast<sockaddr*>(&bind_storage),
527
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 23 taken 1375 times.
1375x bind_len) == SOCKET_ERROR)
528 {
529 svc_.on_completion(&op, ::WSAGetLastError(), 0);
530 return std::noop_coroutine();
531 }
532 }
533
534 1376x auto connect_ex = svc_.connect_ex();
535
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 30 taken 1376 times.
1376x if (!connect_ex)
536 {
537 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
538 return std::noop_coroutine();
539 }
540
541 1376x sockaddr_storage storage{};
542 1376x socklen_t addrlen = detail::to_sockaddr(ep, family_, storage);
543
544
1/1
✓ Branch 31 → 32 taken 1376 times.
1376x 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 1376 times.
✗ Branch 32 → 39 not taken.
1376x if (!result)
549 {
550
1/1
✓ Branch 33 → 34 taken 1376 times.
1376x DWORD err = ::WSAGetLastError();
551
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 39 taken 1376 times.
1376x if (err != ERROR_IO_PENDING)
552 {
553 svc_.on_completion(&op, err, 0);
554 return std::noop_coroutine();
555 }
556 }
557
558 1376x svc_.on_pending(&op);
559 1376x return std::noop_coroutine();
560 }
561
562 inline std::coroutine_handle<>
563 212546x 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 212546 times.
212546x rd_.internal_ptr = shared_from_this();
573
574 212546x auto& op = rd_;
575 212546x op.reset();
576 212546x op.is_read_ = true;
577 212546x op.h = h;
578 212546x op.ex = d;
579 212546x op.ec_out = ec;
580 212546x op.bytes_out = bytes_out;
581 212546x op.start(token);
582
583 212546x svc_.work_started();
584
585 // Prepare buffers
586 212546x capy::mutable_buffer bufs[read_op::max_buffers];
587 212546x op.wsabuf_count =
588 212546x 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 212545 times.
212546x 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 212545 times.
✓ Branch 20 → 21 taken 212545 times.
425090x for (DWORD i = 0; i < op.wsabuf_count; ++i)
599 {
600 212545x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
601 212545x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
602 }
603
604 212545x op.flags = 0;
605
606 425090x int result = ::WSARecv(
607
1/1
✓ Branch 21 → 22 taken 212545 times.
212545x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
608
609
2/2
✓ Branch 22 → 23 taken 3540 times.
✓ Branch 22 → 29 taken 209005 times.
212545x if (result == SOCKET_ERROR)
610 {
611
1/1
✓ Branch 23 → 24 taken 3540 times.
3540x DWORD err = ::WSAGetLastError();
612
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 24 → 29 taken 3537 times.
3540x if (err != WSA_IO_PENDING)
613 {
614 3x svc_.on_completion(&op, err, 0);
615 3x return std::noop_coroutine();
616 }
617 }
618
619 212542x 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 212542 times.
212542x if (op.cancelled.load(std::memory_order_acquire))
623 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
624
625 212542x return std::noop_coroutine();
626 }
627
628 inline std::coroutine_handle<>
629 210886x 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 210886 times.
210886x wr_.internal_ptr = shared_from_this();
639
640 210886x auto& op = wr_;
641 210886x op.reset();
642 210886x op.h = h;
643 210886x op.ex = d;
644 210886x op.ec_out = ec;
645 210886x op.bytes_out = bytes_out;
646 210886x op.start(token);
647
648 210886x svc_.work_started();
649
650 // Prepare buffers
651 210886x capy::mutable_buffer bufs[write_op::max_buffers];
652 210886x op.wsabuf_count =
653 210886x 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 210885 times.
210886x 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 210885 times.
✓ Branch 20 → 21 taken 210885 times.
421770x for (DWORD i = 0; i < op.wsabuf_count; ++i)
663 {
664 210885x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
665 210885x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
666 }
667
668 421770x int result = ::WSASend(
669
1/1
✓ Branch 21 → 22 taken 210885 times.
210885x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
670
671
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 29 taken 210884 times.
210885x 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 210884x 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 210884 times.
210884x if (op.cancelled.load(std::memory_order_acquire))
685 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
686
687 210884x return std::noop_coroutine();
688 }
689
690 inline std::coroutine_handle<>
691 4x 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 4 times.
4x wt_.internal_ptr = shared_from_this();
699
700 4x auto& op = wt_;
701 4x op.reset();
702 4x op.h = h;
703 4x op.ex = d;
704 4x op.ec_out = ec;
705 4x op.bytes_out = nullptr;
706 4x op.empty_buffer = true; // skip EOF translation in invoke_handler
707 4x op.start(token);
708
709 4x svc_.work_started();
710
711
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 15 taken 2 times.
4x if (w == wait_type::write)
712 {
713 // Match asio's IOCP behavior and corosio's reactor contract:
714 // wait_type::write completes immediately on a connected socket.
715 2x svc_.on_completion(&op, 0, 0);
716 2x return std::noop_coroutine();
717 }
718
719
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 31 not taken.
2x 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 2x op.wsabuf = WSABUF{0, nullptr};
726 2x op.flags = 0;
727
728 2x int result = ::WSARecv(
729 socket_, &op.wsabuf, 1, nullptr, &op.flags, &op, nullptr);
730
731
1/2
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 24 not taken.
2x if (result == SOCKET_ERROR)
732 {
733 2x DWORD err = ::WSAGetLastError();
734
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 24 taken 2 times.
2x if (err != WSA_IO_PENDING)
735 {
736 svc_.on_completion(&op, err, 0);
737 return std::noop_coroutine();
738 }
739 }
740
741 2x 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 2 times.
2x if (op.cancelled.load(std::memory_order_acquire))
745 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
746
747 2x return std::noop_coroutine();
748 }
749
750 // wait_type::error: route through the auxiliary select reactor.
751 svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
752 return std::noop_coroutine();
753 }
754
755 inline void
756 2142x win_tcp_socket_internal::cancel() noexcept
757 {
758
1/2
✓ Branch 2 → 3 taken 2142 times.
✗ Branch 2 → 4 not taken.
2142x if (socket_ != INVALID_SOCKET)
759 {
760 2142x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
761 }
762
763 2142x conn_.request_cancel();
764 2142x rd_.request_cancel();
765 2142x wr_.request_cancel();
766 2142x 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 2142x svc_.scheduler().cancel_wait_if_constructed(&wt_);
772 2142x }
773
774 inline void
775 12661x 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 12661x wt_.request_cancel();
783 12661x svc_.scheduler().cancel_wait_if_constructed(&wt_);
784
785
2/2
✓ Branch 5 → 6 taken 2768 times.
✓ Branch 5 → 9 taken 9893 times.
12661x if (socket_ != INVALID_SOCKET)
786 {
787 2768x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
788 2768x ::closesocket(socket_);
789 2768x socket_ = INVALID_SOCKET;
790 }
791
792 12661x family_ = AF_UNSPEC;
793
794 // Clear cached endpoints
795 12661x local_endpoint_ = endpoint{};
796 12661x remote_endpoint_ = endpoint{};
797 12661x }
798
799 // win_tcp_socket
800
801 4252x inline win_tcp_socket::win_tcp_socket(
802 4252x std::shared_ptr<win_tcp_socket_internal> internal) noexcept
803 4252x : internal_(std::move(internal))
804 {
805 4252x }
806
807 inline void
808 4252x win_tcp_socket::close_internal() noexcept
809 {
810
1/2
✓ Branch 3 → 4 taken 4252 times.
✗ Branch 3 → 7 not taken.
4252x if (internal_)
811 {
812 4252x internal_->close_socket();
813 4252x internal_.reset();
814 }
815 4252x }
816
817 inline std::coroutine_handle<>
818 1376x 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 1376 times.
1376x return internal_->connect(h, d, ep, token, ec);
826 }
827
828 inline std::coroutine_handle<>
829 212546x 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 212546 times.
212546x return internal_->read_some(h, d, buf, token, ec, bytes);
838 }
839
840 inline std::coroutine_handle<>
841 210886x 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 210886 times.
210886x return internal_->write_some(h, d, buf, token, ec, bytes);
850 }
851
852 inline std::coroutine_handle<>
853 4x 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 4 times.
4x return internal_->wait(h, d, w, token, ec);
861 }
862
863 inline std::error_code
864 3x win_tcp_socket::shutdown(tcp_socket::shutdown_type what) noexcept
865 {
866 int how;
867
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 1 time.
✗ Branch 2 → 6 not taken.
3x switch (what)
868 {
869 1x case tcp_socket::shutdown_receive:
870 1x how = SD_RECEIVE;
871 1x break;
872 1x case tcp_socket::shutdown_send:
873 1x how = SD_SEND;
874 1x break;
875 1x case tcp_socket::shutdown_both:
876 1x how = SD_BOTH;
877 1x break;
878 default:
879 return make_err(WSAEINVAL);
880 }
881
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 3 times.
3x if (::shutdown(internal_->native_handle(), how) != 0)
882 return make_err(WSAGetLastError());
883 3x return {};
884 }
885
886 inline native_handle_type
887 14879x win_tcp_socket::native_handle() const noexcept
888 {
889 14879x return static_cast<native_handle_type>(internal_->native_handle());
890 }
891
892 inline std::error_code
893 2410x win_tcp_socket::set_option(
894 int level, int optname, void const* data, std::size_t size) noexcept
895 {
896 2410x if (::setsockopt(
897 2410x internal_->native_handle(), level, optname,
898
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 2410 times.
2410x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
899 return make_err(WSAGetLastError());
900 2410x 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 2142x win_tcp_socket::cancel() noexcept
930 {
931 2142x internal_->cancel();
932 2142x }
933
934 inline win_tcp_socket_internal*
935 11149x win_tcp_socket::get_internal() const noexcept
936 {
937 11149x return internal_.get();
938 }
939
940 // win_tcp_service
941
942 567x inline win_tcp_service::win_tcp_service(capy::execution_context& ctx)
943 1134x : sched_(ctx.use_service<win_scheduler>())
944
2/2
✓ Branch 5 → 6 taken 567 times.
✓ Branch 6 → 7 taken 567 times.
567x , iocp_(sched_.native_handle())
945 {
946
1/1
✓ Branch 12 → 13 taken 567 times.
567x load_extension_functions();
947 567x }
948
949 1134x 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 567 times.
567x 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 567 times.
567x for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
959 w = acceptor_wrapper_list_.pop_front())
960 delete w;
961 1134x }
962
963 inline void
964 567x win_tcp_service::shutdown()
965 {
966 567x 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 567 times.
567x 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 567 times.
567x for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
980 impl = acceptor_list_.pop_front())
981 {
982 impl->close_socket();
983 }
984 567x }
985
986 inline io_object::implementation*
987 4252x win_tcp_service::construct()
988 {
989
1/1
✓ Branch 2 → 3 taken 4252 times.
4252x auto internal = std::make_shared<win_tcp_socket_internal>(*this);
990
991 {
992 4252x std::lock_guard<win_mutex> lock(mutex_);
993 4252x socket_list_.push_back(internal.get());
994 4252x }
995
996
1/1
✓ Branch 7 → 8 taken 4252 times.
4252x auto* wrapper = new win_tcp_socket(std::move(internal));
997
998 {
999 4252x std::lock_guard<win_mutex> lock(mutex_);
1000 4252x socket_wrapper_list_.push_back(wrapper);
1001 4252x }
1002
1003 4252x return wrapper;
1004 4252x }
1005
1006 inline void
1007 4252x win_tcp_service::destroy(io_object::implementation* p)
1008 {
1009
1/2
✓ Branch 2 → 3 taken 4252 times.
✗ Branch 2 → 5 not taken.
4252x if (p)
1010 {
1011 4252x auto& wrapper = static_cast<win_tcp_socket&>(*p);
1012 4252x wrapper.close_internal();
1013 4252x destroy_impl(wrapper);
1014 }
1015 4252x }
1016
1017 inline void
1018 7008x win_tcp_service::close(io_object::handle& h)
1019 {
1020 7008x auto& wrapper = static_cast<win_tcp_socket&>(*h.get());
1021 7008x wrapper.get_internal()->close_socket();
1022 7008x }
1023
1024 inline void
1025 4252x win_tcp_service::destroy_impl(win_tcp_socket& impl)
1026 {
1027 {
1028 4252x std::lock_guard<win_mutex> lock(mutex_);
1029 4252x socket_wrapper_list_.remove(&impl);
1030 4252x }
1031
1/2
✓ Branch 5 → 6 taken 4252 times.
✗ Branch 5 → 7 not taken.
4252x delete &impl;
1032 4252x }
1033
1034 inline void
1035 4252x win_tcp_service::unregister_impl(win_tcp_socket_internal& impl)
1036 {
1037 4252x std::lock_guard<win_mutex> lock(mutex_);
1038 4252x socket_list_.remove(&impl);
1039 4252x }
1040
1041 inline std::error_code
1042 1401x win_tcp_service::open_socket(
1043 win_tcp_socket_internal& impl, int family, int type, int protocol)
1044 {
1045 1401x impl.close_socket();
1046
1047 SOCKET sock =
1048 1401x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1049
1050
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1401 times.
1401x if (sock == INVALID_SOCKET)
1051 return make_err(::WSAGetLastError());
1052
1053
2/2
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 10 taken 1394 times.
1401x 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 2802x HANDLE result = ::CreateIoCompletionPort(
1062 1401x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1063
1064
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1401 times.
1401x if (result == nullptr)
1065 {
1066 DWORD dwError = ::GetLastError();
1067 ::closesocket(sock);
1068 return make_err(dwError);
1069 }
1070
1071 1401x impl.socket_ = sock;
1072 1401x impl.family_ = family;
1073 1401x 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 1379x win_tcp_service::native_handle() const noexcept
1100 {
1101 1379x 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 426183x win_tcp_service::on_pending(overlapped_op* op) noexcept
1112 {
1113 426183x sched_.on_pending(op);
1114 426183x }
1115
1116 inline void
1117 8x win_tcp_service::on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept
1118 {
1119 8x sched_.on_completion(op, error, bytes);
1120 8x }
1121
1122 inline void
1123 426193x win_tcp_service::work_started() noexcept
1124 {
1125 426193x sched_.work_started();
1126 426193x }
1127
1128 inline void
1129 win_tcp_service::work_finished() noexcept
1130 {
1131 sched_.work_finished();
1132 }
1133
1134 inline void
1135 567x win_tcp_service::load_extension_functions()
1136 {
1137
1/1
✓ Branch 2 → 3 taken 567 times.
567x 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 567 times.
567x if (sock == INVALID_SOCKET)
1141 return;
1142
1143 567x DWORD bytes = 0;
1144
1145 567x GUID connect_ex_guid = WSAID_CONNECTEX;
1146 567x ::WSAIoctl(
1147 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
1148
1/1
✓ Branch 5 → 6 taken 567 times.
567x sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
1149 nullptr, nullptr);
1150
1151 567x GUID accept_ex_guid = WSAID_ACCEPTEX;
1152 567x ::WSAIoctl(
1153 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
1154
1/1
✓ Branch 6 → 7 taken 567 times.
567x sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
1155 nullptr, nullptr);
1156
1157
1/1
✓ Branch 7 → 8 taken 567 times.
567x ::closesocket(sock);
1158 }
1159
1160 inline void
1161 1341x win_tcp_service::destroy_acceptor_impl(win_tcp_acceptor& impl)
1162 {
1163 {
1164 1341x std::lock_guard<win_mutex> lock(mutex_);
1165 1341x acceptor_wrapper_list_.remove(&impl);
1166 1341x }
1167
1/2
✓ Branch 5 → 6 taken 1341 times.
✗ Branch 5 → 7 not taken.
1341x delete &impl;
1168 1341x }
1169
1170 inline void
1171 1341x win_tcp_service::unregister_acceptor_impl(win_tcp_acceptor_internal& impl)
1172 {
1173 1341x std::lock_guard<win_mutex> lock(mutex_);
1174 1341x acceptor_list_.remove(&impl);
1175 1341x }
1176
1177 inline std::error_code
1178 1336x win_tcp_service::open_acceptor_socket(
1179 win_tcp_acceptor_internal& impl, int family, int type, int protocol)
1180 {
1181 1336x impl.close_socket();
1182
1183 SOCKET sock =
1184 1336x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1185
1186
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1336 times.
1336x if (sock == INVALID_SOCKET)
1187 return make_err(::WSAGetLastError());
1188
1189
2/2
✓ Branch 7 → 8 taken 9 times.
✓ Branch 7 → 10 taken 1327 times.
1336x 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 2672x HANDLE result = ::CreateIoCompletionPort(
1198 1336x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1199
1200
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1336 times.
1336x if (result == nullptr)
1201 {
1202 DWORD dwError = ::GetLastError();
1203 ::closesocket(sock);
1204 return make_err(dwError);
1205 }
1206
1207 1336x impl.socket_ = sock;
1208 1336x return {};
1209 }
1210
1211 inline std::error_code
1212 1335x win_tcp_service::bind_acceptor(win_tcp_acceptor_internal& impl, endpoint ep)
1213 {
1214 1335x SOCKET sock = impl.socket_;
1215
1216 1335x sockaddr_storage storage{};
1217 1335x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1218
1/1
✓ Branch 3 → 4 taken 1335 times.
1335x if (::bind(
1219 sock, reinterpret_cast<sockaddr*>(&storage),
1220
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 7 taken 1331 times.
1335x 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 1331x sockaddr_storage local_storage{};
1225 1331x int local_len = sizeof(local_storage);
1226
1/1
✓ Branch 7 → 8 taken 1331 times.
1331x if (::getsockname(
1227
1/2
✓ Branch 8 → 9 taken 1331 times.
✗ Branch 8 → 11 not taken.
1331x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1228 1331x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1229
1230 1331x return {};
1231 }
1232
1233 inline std::error_code
1234 1324x win_tcp_service::listen_acceptor(win_tcp_acceptor_internal& impl, int backlog)
1235 {
1236 1324x SOCKET sock = impl.socket_;
1237
1238
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1324 times.
1324x if (::listen(sock, backlog) == SOCKET_ERROR)
1239 return make_err(::WSAGetLastError());
1240
1241 1324x return {};
1242 }
1243
1244 // win_tcp_acceptor_internal
1245
1246 1341x inline win_tcp_acceptor_internal::win_tcp_acceptor_internal(win_tcp_service& svc) noexcept
1247 1341x : svc_(svc)
1248 {
1249 1341x }
1250
1251 1341x inline win_tcp_acceptor_internal::~win_tcp_acceptor_internal()
1252 {
1253 1341x svc_.unregister_acceptor_impl(*this);
1254 1341x }
1255
1256 inline win_tcp_service&
1257 12x win_tcp_acceptor_internal::socket_service() noexcept
1258 {
1259 12x return svc_;
1260 }
1261
1262 inline SOCKET
1263 1333x win_tcp_acceptor_internal::native_handle() const noexcept
1264 {
1265 1333x return socket_;
1266 }
1267
1268 inline endpoint
1269 1323x win_tcp_acceptor_internal::local_endpoint() const noexcept
1270 {
1271 1323x return local_endpoint_;
1272 }
1273
1274 inline bool
1275 10711x win_tcp_acceptor_internal::is_open() const noexcept
1276 {
1277 10711x return socket_ != INVALID_SOCKET;
1278 }
1279
1280 inline void
1281 1331x win_tcp_acceptor_internal::set_local_endpoint(endpoint ep) noexcept
1282 {
1283 1331x local_endpoint_ = ep;
1284 1331x }
1285
1286 inline std::coroutine_handle<>
1287 1379x 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 1379 times.
1379x acc_.acceptor_ptr = shared_from_this();
1296
1297 1379x auto& op = acc_;
1298 1379x op.reset();
1299 1379x op.h = h;
1300 1379x op.ex = d;
1301 1379x op.ec_out = ec;
1302 1379x op.impl_out = impl_out;
1303 1379x op.start(token);
1304
1305 1379x svc_.work_started();
1306
1307 // Create wrapper for the peer socket (service owns it)
1308
1/1
✓ Branch 10 → 11 taken 1379 times.
1379x 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 1373 times.
1379x 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 1379 times.
1379x 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 1379 times.
1379x 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 1379 times.
1379x HANDLE result = ::CreateIoCompletionPort(
1325 1379x reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
1326
1327
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 33 taken 1379 times.
1379x 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 1379x op.accepted_socket = accepted;
1338 1379x op.peer_wrapper = &peer_wrapper;
1339 1379x op.listen_socket = socket_;
1340
1341 1379x auto accept_ex = svc_.accept_ex();
1342
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 41 taken 1379 times.
1379x 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 1373 times.
1379x DWORD addr_size = static_cast<DWORD>(
1354 (af == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) + 16);
1355 1379x DWORD bytes_received = 0;
1356
1357 2758x BOOL ok = accept_ex(
1358
1/1
✓ Branch 44 → 45 taken 1379 times.
1379x socket_, accepted, op.addr_buf, 0, addr_size, addr_size,
1359 &bytes_received, &op);
1360
1361
1/2
✓ Branch 45 → 46 taken 1379 times.
✗ Branch 45 → 54 not taken.
1379x if (!ok)
1362 {
1363
1/1
✓ Branch 46 → 47 taken 1379 times.
1379x DWORD err = ::WSAGetLastError();
1364
1/2
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 54 taken 1379 times.
1379x 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 1379x 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 1378 times.
1379x 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 1379x return std::noop_coroutine();
1385 }
1386
1387 inline std::coroutine_handle<>
1388 2x win_tcp_acceptor_internal::wait(
1389 std::coroutine_handle<> h,
1390 capy::executor_ref d,
1391 wait_type w,
1392 std::stop_token token,
1393 std::error_code* ec)
1394 {
1395
1/1
✓ Branch 2 → 3 taken 2 times.
2x wt_.acceptor_ptr = shared_from_this();
1396 2x wt_.listen_socket = socket_;
1397
1398 2x auto& op = wt_;
1399 2x op.reset();
1400 2x op.h = h;
1401 2x op.ex = d;
1402 2x op.ec_out = ec;
1403 2x op.bytes_out = nullptr;
1404 2x op.start(token);
1405
1406 2x svc_.work_started();
1407
1408
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 15 taken 2 times.
2x if (w == wait_type::write)
1409 {
1410 svc_.on_completion(&op, 0, 0);
1411 return std::noop_coroutine();
1412 }
1413
1414 // wait_type::read (incoming connection ready) and wait_type::error
1415 // on the listen socket route through the auxiliary select reactor.
1416 2x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
1417 2x return std::noop_coroutine();
1418 }
1419
1420 inline void
1421 3x win_tcp_acceptor_internal::cancel() noexcept
1422 {
1423
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 4 not taken.
3x if (socket_ != INVALID_SOCKET)
1424 {
1425 3x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1426 }
1427
1428 3x acc_.request_cancel();
1429 3x wt_.request_cancel();
1430 3x svc_.scheduler().cancel_wait_if_constructed(&wt_);
1431 3x }
1432
1433 inline void
1434 5354x win_tcp_acceptor_internal::close_socket() noexcept
1435 {
1436 // Tear down any aux-reactor-parked wait op first.
1437 5354x wt_.request_cancel();
1438 5354x svc_.scheduler().cancel_wait_if_constructed(&wt_);
1439
1440
2/2
✓ Branch 5 → 6 taken 1336 times.
✓ Branch 5 → 9 taken 4018 times.
5354x if (socket_ != INVALID_SOCKET)
1441 {
1442 1336x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1443 1336x ::closesocket(socket_);
1444 1336x socket_ = INVALID_SOCKET;
1445 }
1446
1447 // Clear cached endpoint
1448 5354x local_endpoint_ = endpoint{};
1449 5354x }
1450
1451 // win_tcp_acceptor
1452
1453 1341x inline win_tcp_acceptor::win_tcp_acceptor(
1454 1341x std::shared_ptr<win_tcp_acceptor_internal> internal) noexcept
1455 1341x : internal_(std::move(internal))
1456 {
1457 1341x }
1458
1459 inline void
1460 1341x win_tcp_acceptor::close_internal() noexcept
1461 {
1462
1/2
✓ Branch 3 → 4 taken 1341 times.
✗ Branch 3 → 7 not taken.
1341x if (internal_)
1463 {
1464 1341x internal_->close_socket();
1465 1341x internal_.reset();
1466 }
1467 1341x }
1468
1469 inline std::coroutine_handle<>
1470 1379x 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 1379 times.
1379x return internal_->accept(h, d, token, ec, impl_out);
1478 }
1479
1480 inline std::coroutine_handle<>
1481 2x win_tcp_acceptor::wait(
1482 std::coroutine_handle<> h,
1483 capy::executor_ref d,
1484 wait_type w,
1485 std::stop_token token,
1486 std::error_code* ec)
1487 {
1488
1/1
✓ Branch 4 → 5 taken 2 times.
2x return internal_->wait(h, d, w, token, ec);
1489 }
1490
1491 inline endpoint
1492 1323x win_tcp_acceptor::local_endpoint() const noexcept
1493 {
1494 1323x return internal_->local_endpoint();
1495 }
1496
1497 inline bool
1498 10711x win_tcp_acceptor::is_open() const noexcept
1499 {
1500
3/4
✓ Branch 3 → 4 taken 10711 times.
✗ Branch 3 → 8 not taken.
✓ Branch 6 → 7 taken 8053 times.
✓ Branch 6 → 8 taken 2658 times.
10711x return internal_ && internal_->is_open();
1501 }
1502
1503 inline void
1504 3x win_tcp_acceptor::cancel() noexcept
1505 {
1506 3x internal_->cancel();
1507 3x }
1508
1509 inline std::error_code
1510 1333x win_tcp_acceptor::set_option(
1511 int level, int optname, void const* data, std::size_t size) noexcept
1512 {
1513 1333x if (::setsockopt(
1514 1333x internal_->native_handle(), level, optname,
1515
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1333 times.
1333x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
1516 return make_err(WSAGetLastError());
1517 1333x 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 6672x win_tcp_acceptor::get_internal() const noexcept
1535 {
1536 6672x return internal_.get();
1537 }
1538
1539 // win_tcp_acceptor_service
1540
1541 567x inline win_tcp_acceptor_service::win_tcp_acceptor_service(
1542 567x capy::execution_context& ctx, win_tcp_service& svc)
1543 567x : svc_(svc)
1544 {
1545 (void)ctx;
1546 567x }
1547
1548 inline io_object::implementation*
1549 1341x win_tcp_acceptor_service::construct()
1550 {
1551
1/1
✓ Branch 2 → 3 taken 1341 times.
1341x auto internal = std::make_shared<win_tcp_acceptor_internal>(svc_);
1552
1553 {
1554 1341x std::lock_guard<win_mutex> lock(svc_.mutex_);
1555 1341x svc_.acceptor_list_.push_back(internal.get());
1556 1341x }
1557
1558
1/1
✓ Branch 7 → 8 taken 1341 times.
1341x auto* wrapper = new win_tcp_acceptor(std::move(internal));
1559
1560 {
1561 1341x std::lock_guard<win_mutex> lock(svc_.mutex_);
1562 1341x svc_.acceptor_wrapper_list_.push_back(wrapper);
1563 1341x }
1564
1565 1341x return wrapper;
1566 1341x }
1567
1568 inline void
1569 1341x win_tcp_acceptor_service::destroy(io_object::implementation* p)
1570 {
1571
1/2
✓ Branch 2 → 3 taken 1341 times.
✗ Branch 2 → 5 not taken.
1341x if (p)
1572 {
1573 1341x auto& wrapper = static_cast<win_tcp_acceptor&>(*p);
1574 1341x wrapper.close_internal();
1575 1341x svc_.destroy_acceptor_impl(wrapper);
1576 }
1577 1341x }
1578
1579 inline void
1580 2677x win_tcp_acceptor_service::close(io_object::handle& h)
1581 {
1582 2677x auto& wrapper = static_cast<win_tcp_acceptor&>(*h.get());
1583 2677x wrapper.get_internal()->close_socket();
1584 2677x }
1585
1586 inline std::error_code
1587 1336x win_tcp_acceptor_service::open_acceptor_socket(
1588 tcp_acceptor::implementation& impl, int family, int type, int protocol)
1589 {
1590 1336x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1591 1336x return svc_.open_acceptor_socket(
1592 2672x *wrapper.get_internal(), family, type, protocol);
1593 }
1594
1595 inline std::error_code
1596 1335x win_tcp_acceptor_service::bind_acceptor(
1597 tcp_acceptor::implementation& impl, endpoint ep)
1598 {
1599 1335x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1600 1335x return svc_.bind_acceptor(*wrapper.get_internal(), ep);
1601 }
1602
1603 inline std::error_code
1604 1324x win_tcp_acceptor_service::listen_acceptor(
1605 tcp_acceptor::implementation& impl, int backlog)
1606 {
1607 1324x auto& wrapper = static_cast<win_tcp_acceptor&>(impl);
1608 1324x return svc_.listen_acceptor(*wrapper.get_internal(), backlog);
1609 }
1610
1611 inline void
1612 567x win_tcp_acceptor_service::shutdown()
1613 {
1614 567x }
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