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

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