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

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