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

96.3% Lines (514/534) 98.5% List of functions (64/65) 79.4% Branches (162/204)
win_local_stream_service.hpp
f(x) Functions (65)
Function Calls Lines Branches Blocks
boost::corosio::detail::win_local_stream_service::scheduler() :119 1099x 100.0% 100.0% boost::corosio::detail::local_stream_connect_op::local_stream_connect_op(boost::corosio::detail::win_local_stream_socket_internal&) :144 290x 100.0% 100.0% boost::corosio::detail::local_stream_read_op::local_stream_read_op(boost::corosio::detail::win_local_stream_socket_internal&) :152 290x 100.0% 100.0% boost::corosio::detail::local_stream_write_op::local_stream_write_op(boost::corosio::detail::win_local_stream_socket_internal&) :160 290x 100.0% 100.0% boost::corosio::detail::local_stream_wait_op::local_stream_wait_op(boost::corosio::detail::win_local_stream_socket_internal&) :168 290x 100.0% 100.0% boost::corosio::detail::local_stream_connect_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :181 0 0.0% 0.0% 0.0% boost::corosio::detail::local_stream_read_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :193 2x 100.0% 50.0% 88.9% boost::corosio::detail::local_stream_write_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :205 1x 100.0% 50.0% 88.9% boost::corosio::detail::local_stream_wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :217 1x 100.0% 50.0% 90.9% boost::corosio::detail::local_stream_connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :234 26x 100.0% 82.4% 90.9% boost::corosio::detail::local_stream_read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :278 21x 100.0% 100.0% 84.6% boost::corosio::detail::local_stream_write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :302 18x 100.0% 100.0% 84.6% boost::corosio::detail::local_stream_wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :326 9x 100.0% 100.0% 84.6% boost::corosio::detail::win_local_stream_socket_internal::win_local_stream_socket_internal(boost::corosio::detail::win_local_stream_service&) :349 290x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::~win_local_stream_socket_internal() :359 290x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::native_handle() const :365 813x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::local_endpoint() const :371 2x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::remote_endpoint() const :377 2x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::is_open() const :383 25x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::set_socket(unsigned long long) :389 15x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::set_endpoints(boost::corosio::local_endpoint, boost::corosio::local_endpoint) :395 36x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::connect(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::local_endpoint, std::stop_token, std::error_code*) :403 26x 97.0% 77.8% 83.7% boost::corosio::detail::win_local_stream_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*) :473 21x 97.2% 87.5% 85.4% boost::corosio::detail::win_local_stream_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*) :543 18x 97.0% 87.5% 85.4% boost::corosio::detail::win_local_stream_socket_internal::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :609 9x 96.6% 81.8% 85.7% boost::corosio::detail::win_local_stream_socket_internal::cancel() :674 10x 100.0% 50.0% 100.0% boost::corosio::detail::win_local_stream_socket_internal::close_socket() :689 779x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::win_local_stream_socket(std::shared_ptr<boost::corosio::detail::win_local_stream_socket_internal>) :715 290x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::close_internal() :722 289x 100.0% 50.0% 100.0% boost::corosio::detail::win_local_stream_socket::connect(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::local_endpoint, std::stop_token, std::error_code*) :732 26x 100.0% 100.0% 80.0% boost::corosio::detail::win_local_stream_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*) :743 21x 100.0% 100.0% 80.0% boost::corosio::detail::win_local_stream_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*) :755 18x 100.0% 100.0% 80.0% boost::corosio::detail::win_local_stream_socket::wait(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :767 9x 100.0% 100.0% 80.0% boost::corosio::detail::win_local_stream_socket::shutdown(boost::corosio::shutdown_type) :778 4x 87.5% 83.3% 69.2% boost::corosio::detail::win_local_stream_socket::native_handle() const :802 758x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::release_socket() :808 2x 100.0% 50.0% 100.0% boost::corosio::detail::win_local_stream_socket::set_option(int, int, void const*, unsigned long long) :826 3x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::get_option(int, int, void*, unsigned long long*) const :837 2x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::local_endpoint() const :850 2x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::remote_endpoint() const :856 2x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::cancel() :862 8x 100.0% 100.0% boost::corosio::detail::win_local_stream_socket::get_internal() const :868 550x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::win_local_stream_service(boost::capy::execution_context&, boost::corosio::detail::win_tcp_service&) :877 1247x 100.0% 100.0% 78.6% boost::corosio::detail::win_local_stream_service::~win_local_stream_service() :885 2494x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::shutdown() :897 1247x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_service::construct() :915 290x 100.0% 100.0% 89.5% boost::corosio::detail::win_local_stream_service::destroy(boost::corosio::io_object::implementation*) :935 288x 100.0% 50.0% 100.0% boost::corosio::detail::win_local_stream_service::close(boost::corosio::io_object::handle&) :946 382x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::destroy_impl(boost::corosio::detail::win_local_stream_socket&) :953 288x 100.0% 50.0% 100.0% boost::corosio::detail::win_local_stream_service::unregister_impl(boost::corosio::detail::win_local_stream_socket_internal&) :963 290x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::open_socket(boost::corosio::local_stream_socket::implementation&, int, int, int) :971 44x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::assign_socket(boost::corosio::local_stream_socket::implementation&, unsigned long long) :983 94x 91.7% 77.3% 85.7% boost::corosio::detail::win_local_stream_service::open_socket_internal(boost::corosio::detail::win_local_stream_socket_internal&, int, int, int) :1037 44x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_service::native_handle() const :1065 24x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::connect_ex() const :1071 25x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::accept_ex() const :1077 23x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::on_pending(boost::corosio::detail::overlapped_op*) :1089 77x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1095 22x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::work_started() :1102 106x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::destroy_acceptor_impl(boost::corosio::detail::win_local_stream_acceptor&) :1114 90x 100.0% 50.0% 100.0% boost::corosio::detail::win_local_stream_service::unregister_acceptor_impl(boost::corosio::detail::win_local_stream_acceptor_internal&) :1124 90x 100.0% 100.0% boost::corosio::detail::win_local_stream_service::open_acceptor_socket(boost::corosio::detail::win_local_stream_acceptor_internal&, int, int, int) :1132 61x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_service::assign_acceptor_socket(boost::corosio::detail::win_local_stream_acceptor_internal&, unsigned long long) :1161 24x 92.9% 84.2% 76.0% boost::corosio::detail::win_local_stream_service::bind_acceptor(boost::corosio::detail::win_local_stream_acceptor_internal&, boost::corosio::local_endpoint) :1206 45x 100.0% 100.0% 100.0% boost::corosio::detail::win_local_stream_service::listen_acceptor(boost::corosio::detail::win_local_stream_acceptor_internal&, int) :1227 36x 100.0% 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_LOCAL_STREAM_SERVICE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_LOCAL_STREAM_SERVICE_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_HAS_IOCP
16
17 #include <boost/corosio/detail/config.hpp>
18 #include <boost/corosio/detail/local_stream_service.hpp>
19
20 #include <boost/corosio/native/detail/iocp/win_dissociate.hpp>
21 #include <boost/corosio/native/detail/iocp/win_local_stream_acceptor.hpp>
22 #include <boost/corosio/native/detail/iocp/win_local_stream_socket.hpp>
23 #include <boost/corosio/native/detail/iocp/win_tcp_service.hpp>
24 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
25 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
26 #include <boost/corosio/native/detail/iocp/win_mutex.hpp>
27 #include <boost/corosio/native/detail/iocp/win_wsa_init.hpp>
28
29 #include <boost/corosio/native/detail/endpoint_convert.hpp>
30 #include <boost/corosio/native/detail/make_err.hpp>
31 #include <boost/corosio/detail/dispatch_coro.hpp>
32
33 #include <Ws2tcpip.h>
34
35 namespace boost::corosio::detail {
36
37 class win_local_stream_acceptor;
38 class win_local_stream_acceptor_internal;
39 class win_local_stream_acceptor_service;
40
41 /* IOCP local stream socket service.
42
43 Inherits from local_stream_service to enable runtime polymorphism
44 via use_service<local_stream_service>(). Reuses the ConnectEx /
45 AcceptEx function pointers already loaded by win_tcp_service.
46 */
47 class BOOST_COROSIO_DECL win_local_stream_service final
48 : private win_wsa_init
49 , public local_stream_service
50 {
51 public:
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 explicit win_local_stream_service(
59 capy::execution_context& ctx, win_tcp_service& tcp_svc);
60
61 ~win_local_stream_service();
62
63 win_local_stream_service(win_local_stream_service const&) = delete;
64 win_local_stream_service&
65 operator=(win_local_stream_service const&) = delete;
66
67 void shutdown() override;
68
69 std::error_code open_socket(
70 local_stream_socket::implementation& impl,
71 int family,
72 int type,
73 int protocol) override;
74
75 std::error_code assign_socket(
76 local_stream_socket::implementation& impl,
77 native_handle_type fd) override;
78
79 void destroy_impl(win_local_stream_socket& impl);
80
81 void unregister_impl(win_local_stream_socket_internal& impl);
82
83 std::error_code open_socket_internal(
84 win_local_stream_socket_internal& impl,
85 int family,
86 int type,
87 int protocol);
88
89 void destroy_acceptor_impl(win_local_stream_acceptor& impl);
90
91 void unregister_acceptor_impl(win_local_stream_acceptor_internal& impl);
92
93 std::error_code open_acceptor_socket(
94 win_local_stream_acceptor_internal& impl,
95 int family,
96 int type,
97 int protocol);
98
99 std::error_code assign_acceptor_socket(
100 win_local_stream_acceptor_internal& impl, native_handle_type fd);
101
102 std::error_code bind_acceptor(
103 win_local_stream_acceptor_internal& impl, corosio::local_endpoint ep);
104
105 std::error_code
106 listen_acceptor(win_local_stream_acceptor_internal& impl, int backlog);
107
108 void* native_handle() const noexcept;
109 LPFN_CONNECTEX connect_ex() const noexcept;
110 LPFN_ACCEPTEX accept_ex() const noexcept;
111
112 void post(overlapped_op* op);
113 void on_pending(overlapped_op* op) noexcept;
114 void on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept;
115 void work_started() noexcept;
116 void work_finished() noexcept;
117
118 /** Return the owning IOCP scheduler. */
119 1099x win_scheduler& scheduler() noexcept
120 {
121 1099x return sched_;
122 }
123
124 private:
125 friend class win_local_stream_acceptor_service;
126
127 win_tcp_service& tcp_svc_;
128 win_scheduler& sched_;
129 BOOST_COROSIO_MSVC_WARNING_PUSH
130 BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) // detail:: members, dll-interface
131 win_mutex mutex_;
132 intrusive_list<win_local_stream_socket_internal> socket_list_;
133 intrusive_list<win_local_stream_acceptor_internal> acceptor_list_;
134 intrusive_list<win_local_stream_socket> socket_wrapper_list_;
135 intrusive_list<win_local_stream_acceptor> acceptor_wrapper_list_;
136 BOOST_COROSIO_MSVC_WARNING_POP
137 void* iocp_;
138 };
139
140 // ============================================================
141 // Operation constructors
142 // ============================================================
143
144 290x inline local_stream_connect_op::local_stream_connect_op(
145 290x win_local_stream_socket_internal& internal_) noexcept
146 : overlapped_op(&do_complete)
147 290x , internal(internal_)
148 {
149 290x cancel_func_ = &do_cancel_impl;
150 290x }
151
152 290x inline local_stream_read_op::local_stream_read_op(
153 290x win_local_stream_socket_internal& internal_) noexcept
154 : overlapped_op(&do_complete)
155 290x , internal(internal_)
156 {
157 290x cancel_func_ = &do_cancel_impl;
158 290x }
159
160 290x inline local_stream_write_op::local_stream_write_op(
161 290x win_local_stream_socket_internal& internal_) noexcept
162 : overlapped_op(&do_complete)
163 290x , internal(internal_)
164 {
165 290x cancel_func_ = &do_cancel_impl;
166 290x }
167
168 290x inline local_stream_wait_op::local_stream_wait_op(
169 290x win_local_stream_socket_internal& internal_) noexcept
170 : overlapped_op(&do_complete)
171 290x , internal(internal_)
172 {
173 290x cancel_func_ = &do_cancel_impl;
174 290x }
175
176 // ============================================================
177 // Cancellation functions
178 // ============================================================
179
180 inline void
181 local_stream_connect_op::do_cancel_impl(overlapped_op* base) noexcept
182 {
183 auto* op = static_cast<local_stream_connect_op*>(base);
184 op->cancelled.store(true, std::memory_order_release);
185 if (op->internal.is_open())
186 {
187 ::CancelIoEx(
188 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
189 }
190 }
191
192 inline void
193 2x local_stream_read_op::do_cancel_impl(overlapped_op* base) noexcept
194 {
195 2x auto* op = static_cast<local_stream_read_op*>(base);
196 2x op->cancelled.store(true, std::memory_order_release);
197
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 10 not taken.
2x if (op->internal.is_open())
198 {
199
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
4x ::CancelIoEx(
200 2x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
201 }
202 2x }
203
204 inline void
205 1x local_stream_write_op::do_cancel_impl(overlapped_op* base) noexcept
206 {
207 1x auto* op = static_cast<local_stream_write_op*>(base);
208 1x op->cancelled.store(true, std::memory_order_release);
209
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
210 {
211
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
212 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
213 }
214 1x }
215
216 inline void
217 1x local_stream_wait_op::do_cancel_impl(overlapped_op* base) noexcept
218 {
219 1x auto* op = static_cast<local_stream_wait_op*>(base);
220 1x op->cancelled.store(true, std::memory_order_release);
221
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
222 {
223
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
224 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
225 }
226 1x op->internal.svc_.scheduler().cancel_wait(op);
227 1x }
228
229 // ============================================================
230 // connect_op completion handler
231 // ============================================================
232
233 inline void
234 26x local_stream_connect_op::do_complete(
235 void* owner,
236 scheduler_op* base,
237 std::uint32_t /*bytes*/,
238 std::uint32_t /*error*/)
239 {
240 26x auto* op = static_cast<local_stream_connect_op*>(base);
241
242
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 6 taken 25 times.
26x if (!owner)
243 {
244 1x op->cleanup_only();
245 1x op->internal_ptr.reset();
246 1x return;
247 }
248
249 bool success =
250
3/4
✓ Branch 6 → 7 taken 21 times.
✓ Branch 6 → 10 taken 4 times.
✓ Branch 8 → 9 taken 21 times.
✗ Branch 8 → 10 not taken.
25x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
251
5/6
✓ Branch 11 → 12 taken 21 times.
✓ Branch 11 → 15 taken 4 times.
✓ Branch 13 → 14 taken 21 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 21 times.
✓ Branch 16 → 25 taken 4 times.
25x if (success && op->internal.is_open())
252 {
253 // Required after ConnectEx
254
1/1
✓ Branch 18 → 19 taken 21 times.
21x ::setsockopt(
255 21x op->internal.native_handle(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
256 nullptr, 0);
257
258 21x corosio::local_endpoint local_ep;
259 21x sockaddr_storage local_storage{};
260 21x int local_len = sizeof(local_storage);
261
1/1
✓ Branch 20 → 21 taken 21 times.
21x if (::getsockname(
262 21x op->internal.native_handle(),
263
1/2
✓ Branch 21 → 22 taken 21 times.
✗ Branch 21 → 23 not taken.
21x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
264 21x local_ep = from_sockaddr_local(
265 local_storage, static_cast<socklen_t>(local_len));
266 21x op->internal.set_endpoints(local_ep, op->target_endpoint);
267 }
268
269 25x auto prevent_premature_destruction = std::move(op->internal_ptr);
270
1/1
✓ Branch 27 → 28 taken 25 times.
25x op->invoke_handler();
271 25x }
272
273 // ============================================================
274 // read_op completion handler
275 // ============================================================
276
277 inline void
278 21x local_stream_read_op::do_complete(
279 void* owner,
280 scheduler_op* base,
281 std::uint32_t /*bytes*/,
282 std::uint32_t /*error*/)
283 {
284 21x auto* op = static_cast<local_stream_read_op*>(base);
285
286
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 6 taken 18 times.
21x if (!owner)
287 {
288 3x op->cleanup_only();
289 3x op->internal_ptr.reset();
290 3x return;
291 }
292
293 18x auto prevent_premature_destruction = std::move(op->internal_ptr);
294
1/1
✓ Branch 8 → 9 taken 18 times.
18x op->invoke_handler();
295 18x }
296
297 // ============================================================
298 // write_op completion handler
299 // ============================================================
300
301 inline void
302 18x local_stream_write_op::do_complete(
303 void* owner,
304 scheduler_op* base,
305 std::uint32_t /*bytes*/,
306 std::uint32_t /*error*/)
307 {
308 18x auto* op = static_cast<local_stream_write_op*>(base);
309
310
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 6 taken 17 times.
18x if (!owner)
311 {
312 1x op->cleanup_only();
313 1x op->internal_ptr.reset();
314 1x return;
315 }
316
317 17x auto prevent_premature_destruction = std::move(op->internal_ptr);
318
1/1
✓ Branch 8 → 9 taken 17 times.
17x op->invoke_handler();
319 17x }
320
321 // ============================================================
322 // wait_op completion handler
323 // ============================================================
324
325 inline void
326 9x local_stream_wait_op::do_complete(
327 void* owner,
328 scheduler_op* base,
329 std::uint32_t /*bytes*/,
330 std::uint32_t /*error*/)
331 {
332 9x auto* op = static_cast<local_stream_wait_op*>(base);
333
334
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 6 taken 8 times.
9x if (!owner)
335 {
336 1x op->cleanup_only();
337 1x op->internal_ptr.reset();
338 1x return;
339 }
340
341 8x auto prevent_premature_destruction = std::move(op->internal_ptr);
342
1/1
✓ Branch 8 → 9 taken 8 times.
8x op->invoke_handler();
343 8x }
344
345 // ============================================================
346 // win_local_stream_socket_internal
347 // ============================================================
348
349 290x inline win_local_stream_socket_internal::win_local_stream_socket_internal(
350 290x win_local_stream_service& svc) noexcept
351 290x : svc_(svc)
352 290x , conn_(*this)
353 290x , rd_(*this)
354 290x , wr_(*this)
355 580x , wt_(*this)
356 {
357 290x }
358
359 290x inline win_local_stream_socket_internal::~win_local_stream_socket_internal()
360 {
361 290x svc_.unregister_impl(*this);
362 290x }
363
364 inline SOCKET
365 813x win_local_stream_socket_internal::native_handle() const noexcept
366 {
367 813x return socket_;
368 }
369
370 inline corosio::local_endpoint
371 2x win_local_stream_socket_internal::local_endpoint() const noexcept
372 {
373 2x return local_endpoint_;
374 }
375
376 inline corosio::local_endpoint
377 2x win_local_stream_socket_internal::remote_endpoint() const noexcept
378 {
379 2x return remote_endpoint_;
380 }
381
382 inline bool
383 25x win_local_stream_socket_internal::is_open() const noexcept
384 {
385 25x return socket_ != INVALID_SOCKET;
386 }
387
388 inline void
389 15x win_local_stream_socket_internal::set_socket(SOCKET s) noexcept
390 {
391 15x socket_ = s;
392 15x }
393
394 inline void
395 36x win_local_stream_socket_internal::set_endpoints(
396 corosio::local_endpoint local, corosio::local_endpoint remote) noexcept
397 {
398 36x local_endpoint_ = local;
399 36x remote_endpoint_ = remote;
400 36x }
401
402 inline std::coroutine_handle<>
403 26x win_local_stream_socket_internal::connect(
404 std::coroutine_handle<> h,
405 capy::executor_ref d,
406 corosio::local_endpoint ep,
407 std::stop_token token,
408 std::error_code* ec)
409 {
410
1/1
✓ Branch 2 → 3 taken 26 times.
26x conn_.internal_ptr = shared_from_this();
411
412 26x auto& op = conn_;
413 26x op.reset();
414 26x op.h = h;
415 26x op.ex = d;
416 26x op.ec_out = ec;
417 26x op.target_endpoint = ep;
418 26x op.start(token);
419
420 26x svc_.work_started();
421
422 // ConnectEx requires the socket to be bound. For AF_UNIX,
423 // bind to a family-only sockaddr_un (empty path).
424
1/2
✓ Branch 9 → 10 taken 26 times.
✗ Branch 9 → 18 not taken.
26x if (local_endpoint_.empty())
425 {
426 26x un_sa_t bind_sa{};
427 26x bind_sa.sun_family = AF_UNIX;
428 26x socklen_t bind_len =
429 static_cast<socklen_t>(offsetof(un_sa_t, sun_path));
430
431
3/3
✓ Branch 10 → 11 taken 26 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 17 taken 25 times.
26x if (::bind(socket_, reinterpret_cast<sockaddr*>(&bind_sa), bind_len) ==
432 SOCKET_ERROR)
433 {
434
1/1
✓ Branch 12 → 13 taken 1 time.
1x svc_.on_completion(&op, ::WSAGetLastError(), 0);
435 1x return std::noop_coroutine();
436 }
437 }
438
439 25x auto connect_ex = svc_.connect_ex();
440
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 24 taken 24 times.
25x if (!connect_ex)
441 {
442 1x svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
443 1x return std::noop_coroutine();
444 }
445
446 24x sockaddr_storage storage{};
447 24x socklen_t addrlen = detail::to_sockaddr(ep, storage);
448
449
1/1
✓ Branch 25 → 26 taken 24 times.
24x BOOL result = connect_ex(
450 socket_, reinterpret_cast<sockaddr*>(&storage),
451 static_cast<int>(addrlen), nullptr, 0, nullptr, &op);
452
453
1/2
✓ Branch 26 → 27 taken 24 times.
✗ Branch 26 → 33 not taken.
24x if (!result)
454 {
455
1/1
✓ Branch 27 → 28 taken 24 times.
24x DWORD err = ::WSAGetLastError();
456
2/2
✓ Branch 28 → 29 taken 2 times.
✓ Branch 28 → 33 taken 22 times.
24x if (err != ERROR_IO_PENDING)
457 {
458 2x svc_.on_completion(&op, err, 0);
459 2x return std::noop_coroutine();
460 }
461 }
462
463 22x svc_.on_pending(&op);
464
465 // Re-check cancellation after I/O is pending
466
1/2
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 22 times.
22x if (op.cancelled.load(std::memory_order_acquire))
467 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
468
469 22x return std::noop_coroutine();
470 }
471
472 inline std::coroutine_handle<>
473 21x win_local_stream_socket_internal::read_some(
474 std::coroutine_handle<> h,
475 capy::executor_ref d,
476 buffer_param param,
477 std::stop_token token,
478 std::error_code* ec,
479 std::size_t* bytes_out)
480 {
481
1/1
✓ Branch 2 → 3 taken 21 times.
21x rd_.internal_ptr = shared_from_this();
482
483 21x auto& op = rd_;
484 21x op.reset();
485 21x op.is_read = true;
486 21x op.h = h;
487 21x op.ex = d;
488 21x op.ec_out = ec;
489 21x op.bytes_out = bytes_out;
490 21x op.start(token);
491
492 21x svc_.work_started();
493
494 // Closed-object contract: complete with bad_file_descriptor
495 // without touching the kernel.
496
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 19 times.
21x if (socket_ == INVALID_SOCKET)
497 {
498 2x svc_.on_completion(&op, WSAEBADF, 0);
499 2x return std::noop_coroutine();
500 }
501
502 19x capy::mutable_buffer bufs[local_stream_read_op::max_buffers];
503 19x op.wsabuf_count = static_cast<DWORD>(
504 19x param.copy_to(bufs, local_stream_read_op::max_buffers));
505
506
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 19 taken 18 times.
19x if (op.wsabuf_count == 0)
507 {
508 1x op.empty_buffer = true;
509 1x svc_.on_completion(&op, 0, 0);
510 1x return std::noop_coroutine();
511 }
512
513
2/2
✓ Branch 23 → 20 taken 18 times.
✓ Branch 23 → 24 taken 18 times.
36x for (DWORD i = 0; i < op.wsabuf_count; ++i)
514 {
515 18x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
516 18x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
517 }
518
519 18x op.flags = 0;
520
521 36x int result = ::WSARecv(
522
1/1
✓ Branch 24 → 25 taken 18 times.
18x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
523
524
2/2
✓ Branch 25 → 26 taken 15 times.
✓ Branch 25 → 32 taken 3 times.
18x if (result == SOCKET_ERROR)
525 {
526
1/1
✓ Branch 26 → 27 taken 15 times.
15x DWORD err = ::WSAGetLastError();
527
2/2
✓ Branch 27 → 28 taken 1 time.
✓ Branch 27 → 32 taken 14 times.
15x if (err != WSA_IO_PENDING)
528 {
529 1x svc_.on_completion(&op, err, 0);
530 1x return std::noop_coroutine();
531 }
532 }
533
534 17x svc_.on_pending(&op);
535
536
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 17 times.
17x if (op.cancelled.load(std::memory_order_acquire))
537 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
538
539 17x return std::noop_coroutine();
540 }
541
542 inline std::coroutine_handle<>
543 18x win_local_stream_socket_internal::write_some(
544 std::coroutine_handle<> h,
545 capy::executor_ref d,
546 buffer_param param,
547 std::stop_token token,
548 std::error_code* ec,
549 std::size_t* bytes_out)
550 {
551
1/1
✓ Branch 2 → 3 taken 18 times.
18x wr_.internal_ptr = shared_from_this();
552
553 18x auto& op = wr_;
554 18x op.reset();
555 18x op.h = h;
556 18x op.ex = d;
557 18x op.ec_out = ec;
558 18x op.bytes_out = bytes_out;
559 18x op.start(token);
560
561 18x svc_.work_started();
562
563 // Closed-object contract: complete with bad_file_descriptor
564 // without touching the kernel.
565
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 16 times.
18x if (socket_ == INVALID_SOCKET)
566 {
567 2x svc_.on_completion(&op, WSAEBADF, 0);
568 2x return std::noop_coroutine();
569 }
570
571 16x capy::mutable_buffer bufs[local_stream_write_op::max_buffers];
572 16x op.wsabuf_count = static_cast<DWORD>(
573 16x param.copy_to(bufs, local_stream_write_op::max_buffers));
574
575
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 19 taken 15 times.
16x if (op.wsabuf_count == 0)
576 {
577 1x svc_.on_completion(&op, 0, 0);
578 1x return std::noop_coroutine();
579 }
580
581
2/2
✓ Branch 23 → 20 taken 15 times.
✓ Branch 23 → 24 taken 15 times.
30x for (DWORD i = 0; i < op.wsabuf_count; ++i)
582 {
583 15x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
584 15x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
585 }
586
587 30x int result = ::WSASend(
588
1/1
✓ Branch 24 → 25 taken 15 times.
15x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
589
590
2/2
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 32 taken 13 times.
15x if (result == SOCKET_ERROR)
591 {
592
1/1
✓ Branch 26 → 27 taken 2 times.
2x DWORD err = ::WSAGetLastError();
593
2/2
✓ Branch 27 → 28 taken 1 time.
✓ Branch 27 → 32 taken 1 time.
2x if (err != WSA_IO_PENDING)
594 {
595 1x svc_.on_completion(&op, err, 0);
596 1x return std::noop_coroutine();
597 }
598 }
599
600 14x svc_.on_pending(&op);
601
602
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 14 times.
14x if (op.cancelled.load(std::memory_order_acquire))
603 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
604
605 14x return std::noop_coroutine();
606 }
607
608 inline std::coroutine_handle<>
609 9x win_local_stream_socket_internal::wait(
610 std::coroutine_handle<> h,
611 capy::executor_ref d,
612 wait_type w,
613 std::stop_token token,
614 std::error_code* ec)
615 {
616
1/1
✓ Branch 2 → 3 taken 9 times.
9x wt_.internal_ptr = shared_from_this();
617
618 9x auto& op = wt_;
619 9x op.reset();
620 9x op.h = h;
621 9x op.ex = d;
622 9x op.ec_out = ec;
623 9x op.bytes_out = nullptr;
624 9x op.empty_buffer = true;
625 9x op.start(token);
626
627 9x svc_.work_started();
628
629 // Closed-object contract: complete with bad_file_descriptor
630 // without touching the kernel or the wait reactor.
631
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 7 times.
9x if (socket_ == INVALID_SOCKET)
632 {
633 2x svc_.on_completion(&op, WSAEBADF, 0);
634 2x return std::noop_coroutine();
635 }
636
637
2/2
✓ Branch 13 → 14 taken 5 times.
✓ Branch 13 → 29 taken 2 times.
7x if (w == wait_type::read)
638 {
639 // Zero-byte WSARecv — completes when data is available
640 // without consuming any bytes.
641 5x op.wsabuf = WSABUF{0, nullptr};
642 5x op.flags = 0;
643
644 int result =
645 5x ::WSARecv(socket_, &op.wsabuf, 1, nullptr, &op.flags, &op, nullptr);
646
647
1/2
✓ Branch 15 → 16 taken 5 times.
✗ Branch 15 → 22 not taken.
5x if (result == SOCKET_ERROR)
648 {
649 5x DWORD err = ::WSAGetLastError();
650
2/2
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 22 taken 4 times.
5x if (err != WSA_IO_PENDING)
651 {
652 1x svc_.on_completion(&op, err, 0);
653 1x return std::noop_coroutine();
654 }
655 }
656
657 4x svc_.on_pending(&op);
658
659
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 4 times.
4x if (op.cancelled.load(std::memory_order_acquire))
660 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
661
662 4x return std::noop_coroutine();
663 }
664
665 // wait_type::write and wait_type::error: route through the
666 // auxiliary poll reactor. There is no overlapped primitive for
667 // "the send buffer has room" that does not also transfer bytes,
668 // and a write wait must report real writability.
669 2x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
670 2x return std::noop_coroutine();
671 }
672
673 inline void
674 10x win_local_stream_socket_internal::cancel() noexcept
675 {
676
1/2
✓ Branch 2 → 3 taken 10 times.
✗ Branch 2 → 4 not taken.
10x if (socket_ != INVALID_SOCKET)
677 {
678 10x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
679 }
680
681 10x conn_.request_cancel();
682 10x rd_.request_cancel();
683 10x wr_.request_cancel();
684 10x wt_.request_cancel();
685 10x svc_.scheduler().cancel_wait(&wt_);
686 10x }
687
688 inline void
689 779x win_local_stream_socket_internal::close_socket() noexcept
690 {
691 // Flag every op cancelled before closing so a closesocket-delivered
692 // ERROR_NETNAME_DELETED is short-circuited to canceled rather than mapped
693 // to connection_reset by iocp_make_err (see win_tcp_socket close_socket).
694 779x conn_.request_cancel();
695 779x rd_.request_cancel();
696 779x wr_.request_cancel();
697 779x wt_.request_cancel();
698 779x svc_.scheduler().cancel_wait(&wt_);
699
700
2/2
✓ Branch 8 → 9 taken 104 times.
✓ Branch 8 → 12 taken 675 times.
779x if (socket_ != INVALID_SOCKET)
701 {
702 104x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
703 104x ::closesocket(socket_);
704 104x socket_ = INVALID_SOCKET;
705 }
706
707 779x local_endpoint_ = corosio::local_endpoint{};
708 779x remote_endpoint_ = corosio::local_endpoint{};
709 779x }
710
711 // ============================================================
712 // win_local_stream_socket (wrapper)
713 // ============================================================
714
715 290x inline win_local_stream_socket::win_local_stream_socket(
716 290x std::shared_ptr<win_local_stream_socket_internal> internal) noexcept
717 290x : internal_(std::move(internal))
718 {
719 290x }
720
721 inline void
722 289x win_local_stream_socket::close_internal() noexcept
723 {
724
1/2
✓ Branch 3 → 4 taken 289 times.
✗ Branch 3 → 7 not taken.
289x if (internal_)
725 {
726 289x internal_->close_socket();
727 289x internal_.reset();
728 }
729 289x }
730
731 inline std::coroutine_handle<>
732 26x win_local_stream_socket::connect(
733 std::coroutine_handle<> h,
734 capy::executor_ref d,
735 corosio::local_endpoint ep,
736 std::stop_token token,
737 std::error_code* ec)
738 {
739
1/1
✓ Branch 4 → 5 taken 26 times.
26x return internal_->connect(h, d, ep, token, ec);
740 }
741
742 inline std::coroutine_handle<>
743 21x win_local_stream_socket::read_some(
744 std::coroutine_handle<> h,
745 capy::executor_ref d,
746 buffer_param buf,
747 std::stop_token token,
748 std::error_code* ec,
749 std::size_t* bytes)
750 {
751
1/1
✓ Branch 4 → 5 taken 21 times.
21x return internal_->read_some(h, d, buf, token, ec, bytes);
752 }
753
754 inline std::coroutine_handle<>
755 18x win_local_stream_socket::write_some(
756 std::coroutine_handle<> h,
757 capy::executor_ref d,
758 buffer_param buf,
759 std::stop_token token,
760 std::error_code* ec,
761 std::size_t* bytes)
762 {
763
1/1
✓ Branch 4 → 5 taken 18 times.
18x return internal_->write_some(h, d, buf, token, ec, bytes);
764 }
765
766 inline std::coroutine_handle<>
767 9x win_local_stream_socket::wait(
768 std::coroutine_handle<> h,
769 capy::executor_ref d,
770 wait_type w,
771 std::stop_token token,
772 std::error_code* ec)
773 {
774
1/1
✓ Branch 4 → 5 taken 9 times.
9x return internal_->wait(h, d, w, token, ec);
775 }
776
777 inline std::error_code
778 4x win_local_stream_socket::shutdown(
779 local_stream_socket::shutdown_type what) noexcept
780 {
781 int how;
782
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 2 times.
✓ Branch 2 → 5 taken 1 time.
✗ Branch 2 → 6 not taken.
4x switch (what)
783 {
784 1x case local_stream_socket::shutdown_receive:
785 1x how = SD_RECEIVE;
786 1x break;
787 2x case local_stream_socket::shutdown_send:
788 2x how = SD_SEND;
789 2x break;
790 1x case local_stream_socket::shutdown_both:
791 1x how = SD_BOTH;
792 1x break;
793 default:
794 return make_err(WSAEINVAL);
795 }
796
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 13 taken 3 times.
4x if (::shutdown(internal_->native_handle(), how) != 0)
797 1x return make_err(WSAGetLastError());
798 3x return {};
799 }
800
801 inline native_handle_type
802 758x win_local_stream_socket::native_handle() const noexcept
803 {
804 758x return static_cast<native_handle_type>(internal_->native_handle());
805 }
806
807 inline native_handle_type
808 2x win_local_stream_socket::release_socket() noexcept
809 {
810 2x SOCKET s = internal_->socket_;
811
1/2
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 13 not taken.
2x if (s != INVALID_SOCKET)
812 {
813 2x internal_->cancel();
814 // Sever the port association so the descriptor can be
815 // adopted again; best-effort, the caller keeps a working
816 // socket either way.
817 2x dissociate_from_iocp(s);
818 2x internal_->socket_ = INVALID_SOCKET;
819 2x internal_->local_endpoint_ = corosio::local_endpoint{};
820 2x internal_->remote_endpoint_ = corosio::local_endpoint{};
821 }
822 2x return static_cast<native_handle_type>(s);
823 }
824
825 inline std::error_code
826 3x win_local_stream_socket::set_option(
827 int level, int optname, void const* data, std::size_t size) noexcept
828 {
829 3x if (::setsockopt(
830 3x internal_->native_handle(), level, optname,
831
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 8 taken 2 times.
3x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
832 1x return make_err(WSAGetLastError());
833 2x return {};
834 }
835
836 inline std::error_code
837 2x win_local_stream_socket::get_option(
838 int level, int optname, void* data, std::size_t* size) const noexcept
839 {
840 2x int len = static_cast<int>(*size);
841 2x if (::getsockopt(
842 2x internal_->native_handle(), level, optname,
843
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 8 taken 1 time.
2x reinterpret_cast<char*>(data), &len) != 0)
844 1x return make_err(WSAGetLastError());
845 1x *size = static_cast<std::size_t>(len);
846 1x return {};
847 }
848
849 inline corosio::local_endpoint
850 2x win_local_stream_socket::local_endpoint() const noexcept
851 {
852 2x return internal_->local_endpoint();
853 }
854
855 inline corosio::local_endpoint
856 2x win_local_stream_socket::remote_endpoint() const noexcept
857 {
858 2x return internal_->remote_endpoint();
859 }
860
861 inline void
862 8x win_local_stream_socket::cancel() noexcept
863 {
864 8x internal_->cancel();
865 8x }
866
867 inline win_local_stream_socket_internal*
868 550x win_local_stream_socket::get_internal() const noexcept
869 {
870 550x return internal_.get();
871 }
872
873 // ============================================================
874 // win_local_stream_service
875 // ============================================================
876
877 1247x inline win_local_stream_service::win_local_stream_service(
878 1247x capy::execution_context& ctx, win_tcp_service& tcp_svc)
879 1247x : tcp_svc_(tcp_svc)
880 2494x , sched_(ctx.use_service<win_scheduler>())
881
2/2
✓ Branch 4 → 5 taken 1247 times.
✓ Branch 5 → 6 taken 1247 times.
1247x , iocp_(sched_.native_handle())
882 {
883 1247x }
884
885 2494x inline win_local_stream_service::~win_local_stream_service()
886 {
887
2/2
✓ Branch 6 → 3 taken 2 times.
✓ Branch 6 → 7 taken 1247 times.
1249x for (auto* w = socket_wrapper_list_.pop_front(); w != nullptr;
888 2x w = socket_wrapper_list_.pop_front())
889
1/2
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 5 not taken.
2x delete w;
890
891
1/2
✗ Branch 11 → 8 not taken.
✓ Branch 11 → 12 taken 1247 times.
1247x for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
892 w = acceptor_wrapper_list_.pop_front())
893 delete w;
894 2494x }
895
896 inline void
897 1247x win_local_stream_service::shutdown()
898 {
899 1247x std::lock_guard<win_mutex> lock(mutex_);
900
901
2/2
✓ Branch 6 → 4 taken 7 times.
✓ Branch 6 → 7 taken 1247 times.
1254x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
902 7x impl = socket_list_.pop_front())
903 {
904 7x impl->close_socket();
905 }
906
907
2/2
✓ Branch 10 → 8 taken 2 times.
✓ Branch 10 → 11 taken 1247 times.
1249x for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
908 2x impl = acceptor_list_.pop_front())
909 {
910 2x impl->close_socket();
911 }
912 1247x }
913
914 inline io_object::implementation*
915 290x win_local_stream_service::construct()
916 {
917
1/1
✓ Branch 2 → 3 taken 290 times.
290x auto internal = std::make_shared<win_local_stream_socket_internal>(*this);
918
919 {
920 290x std::lock_guard<win_mutex> lock(mutex_);
921 290x socket_list_.push_back(internal.get());
922 290x }
923
924
1/1
✓ Branch 7 → 8 taken 290 times.
290x auto* wrapper = new win_local_stream_socket(std::move(internal));
925
926 {
927 290x std::lock_guard<win_mutex> lock(mutex_);
928 290x socket_wrapper_list_.push_back(wrapper);
929 290x }
930
931 290x return wrapper;
932 290x }
933
934 inline void
935 288x win_local_stream_service::destroy(io_object::implementation* p)
936 {
937
1/2
✓ Branch 2 → 3 taken 288 times.
✗ Branch 2 → 5 not taken.
288x if (p)
938 {
939 288x auto& wrapper = static_cast<win_local_stream_socket&>(*p);
940 288x wrapper.close_internal();
941 288x destroy_impl(wrapper);
942 }
943 288x }
944
945 inline void
946 382x win_local_stream_service::close(io_object::handle& h)
947 {
948 382x auto& wrapper = static_cast<win_local_stream_socket&>(*h.get());
949 382x wrapper.get_internal()->close_socket();
950 382x }
951
952 inline void
953 288x win_local_stream_service::destroy_impl(win_local_stream_socket& impl)
954 {
955 {
956 288x std::lock_guard<win_mutex> lock(mutex_);
957 288x socket_wrapper_list_.remove(&impl);
958 288x }
959
1/2
✓ Branch 5 → 6 taken 288 times.
✗ Branch 5 → 7 not taken.
288x delete &impl;
960 288x }
961
962 inline void
963 290x win_local_stream_service::unregister_impl(
964 win_local_stream_socket_internal& impl)
965 {
966 290x std::lock_guard<win_mutex> lock(mutex_);
967 290x socket_list_.remove(&impl);
968 290x }
969
970 inline std::error_code
971 44x win_local_stream_service::open_socket(
972 local_stream_socket::implementation& impl,
973 int family,
974 int type,
975 int protocol)
976 {
977 44x auto& wrapper = static_cast<win_local_stream_socket&>(impl);
978 44x return open_socket_internal(
979 88x *wrapper.get_internal(), family, type, protocol);
980 }
981
982 inline std::error_code
983 94x win_local_stream_service::assign_socket(
984 local_stream_socket::implementation& impl, native_handle_type fd)
985 {
986 94x auto& wrapper = static_cast<win_local_stream_socket&>(impl);
987 94x auto& internal = *wrapper.get_internal();
988
989 94x SOCKET sock = static_cast<SOCKET>(fd);
990
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 94 times.
94x if (sock == INVALID_SOCKET)
991 return make_err(WSAENOTSOCK);
992
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 93 times.
94x if (sock == internal.socket_)
993 1x return std::make_error_code(std::errc::invalid_argument);
994
995 // SO_PROTOCOL_INFOW works on an unbound socket, unlike getsockname
996 // (WSAEINVAL until bind/connect names it) -- connect_pair hands in
997 // a socket that reached connected state without an explicit bind.
998 93x WSAPROTOCOL_INFOW proto_info{};
999 93x int proto_len = sizeof(proto_info);
1000
1/1
✓ Branch 7 → 8 taken 93 times.
93x if (::getsockopt(
1001 sock, SOL_SOCKET, SO_PROTOCOL_INFOW,
1002
2/2
✓ Branch 8 → 9 taken 27 times.
✓ Branch 8 → 11 taken 66 times.
93x reinterpret_cast<char*>(&proto_info), &proto_len) != 0)
1003
1/1
✓ Branch 9 → 10 taken 27 times.
27x return make_err(::WSAGetLastError());
1004
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 66 times.
66x if (proto_info.iAddressFamily != AF_UNIX)
1005 return make_err(WSAEAFNOSUPPORT);
1006
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 66 times.
66x if (proto_info.iSocketType != SOCK_STREAM)
1007 return make_err(WSAEPROTOTYPE);
1008
1009 // Associate before releasing the held socket: on IOCP nothing
1010 // shares descriptor state the way the reactor path does, so a
1011 // failed association must not cost the caller their old socket.
1012 132x HANDLE result = ::CreateIoCompletionPort(
1013
1/1
✓ Branch 15 → 16 taken 66 times.
66x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1014
2/2
✓ Branch 16 → 17 taken 9 times.
✓ Branch 16 → 19 taken 57 times.
66x if (result == nullptr)
1015
1/1
✓ Branch 17 → 18 taken 9 times.
9x return make_err(::GetLastError());
1016
1017 57x internal.close_socket();
1018 57x internal.socket_ = sock;
1019
1020 57x sockaddr_storage local{};
1021 57x int local_len = sizeof(local);
1022 57x corosio::local_endpoint lep{}, rep{};
1023
2/3
✓ Branch 20 → 21 taken 57 times.
✓ Branch 21 → 22 taken 57 times.
✗ Branch 21 → 23 not taken.
57x if (::getsockname(sock, reinterpret_cast<sockaddr*>(&local), &local_len) ==
1024 0)
1025 57x lep = from_sockaddr_local(local, static_cast<socklen_t>(local_len));
1026 57x sockaddr_storage remote{};
1027 57x int remote_len = sizeof(remote);
1028
1/1
✓ Branch 23 → 24 taken 57 times.
57x if (::getpeername(
1029
1/2
✓ Branch 24 → 25 taken 57 times.
✗ Branch 24 → 26 not taken.
57x sock, reinterpret_cast<sockaddr*>(&remote), &remote_len) == 0)
1030 57x rep = from_sockaddr_local(remote, static_cast<socklen_t>(remote_len));
1031 57x internal.local_endpoint_ = lep;
1032 57x internal.remote_endpoint_ = rep;
1033 57x return {};
1034 }
1035
1036 inline std::error_code
1037 44x win_local_stream_service::open_socket_internal(
1038 win_local_stream_socket_internal& impl, int family, int type, int protocol)
1039 {
1040 44x impl.close_socket();
1041
1042 SOCKET sock =
1043 44x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1044
1045
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 7 taken 43 times.
44x if (sock == INVALID_SOCKET)
1046 1x return make_err(::WSAGetLastError());
1047
1048 // No IPV6_V6ONLY for AF_UNIX
1049
1050 86x HANDLE result = ::CreateIoCompletionPort(
1051 43x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1052
1053
2/2
✓ Branch 8 → 9 taken 9 times.
✓ Branch 8 → 12 taken 34 times.
43x if (result == nullptr)
1054 {
1055 9x DWORD dwError = ::GetLastError();
1056 9x ::closesocket(sock);
1057 9x return make_err(dwError);
1058 }
1059
1060 34x impl.socket_ = sock;
1061 34x return {};
1062 }
1063
1064 inline void*
1065 24x win_local_stream_service::native_handle() const noexcept
1066 {
1067 24x return iocp_;
1068 }
1069
1070 inline LPFN_CONNECTEX
1071 25x win_local_stream_service::connect_ex() const noexcept
1072 {
1073 25x return tcp_svc_.connect_ex();
1074 }
1075
1076 inline LPFN_ACCEPTEX
1077 23x win_local_stream_service::accept_ex() const noexcept
1078 {
1079 23x return tcp_svc_.accept_ex();
1080 }
1081
1082 inline void
1083 win_local_stream_service::post(overlapped_op* op)
1084 {
1085 sched_.post(op);
1086 }
1087
1088 inline void
1089 77x win_local_stream_service::on_pending(overlapped_op* op) noexcept
1090 {
1091 77x sched_.on_pending(op);
1092 77x }
1093
1094 inline void
1095 22x win_local_stream_service::on_completion(
1096 overlapped_op* op, DWORD error, DWORD bytes) noexcept
1097 {
1098 22x sched_.on_completion(op, error, bytes);
1099 22x }
1100
1101 inline void
1102 106x win_local_stream_service::work_started() noexcept
1103 {
1104 106x sched_.work_started();
1105 106x }
1106
1107 inline void
1108 win_local_stream_service::work_finished() noexcept
1109 {
1110 sched_.work_finished();
1111 }
1112
1113 inline void
1114 90x win_local_stream_service::destroy_acceptor_impl(win_local_stream_acceptor& impl)
1115 {
1116 {
1117 90x std::lock_guard<win_mutex> lock(mutex_);
1118 90x acceptor_wrapper_list_.remove(&impl);
1119 90x }
1120
1/2
✓ Branch 5 → 6 taken 90 times.
✗ Branch 5 → 7 not taken.
90x delete &impl;
1121 90x }
1122
1123 inline void
1124 90x win_local_stream_service::unregister_acceptor_impl(
1125 win_local_stream_acceptor_internal& impl)
1126 {
1127 90x std::lock_guard<win_mutex> lock(mutex_);
1128 90x acceptor_list_.remove(&impl);
1129 90x }
1130
1131 inline std::error_code
1132 61x win_local_stream_service::open_acceptor_socket(
1133 win_local_stream_acceptor_internal& impl,
1134 int family,
1135 int type,
1136 int protocol)
1137 {
1138 61x impl.close_socket();
1139
1140 SOCKET sock =
1141 61x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1142
1143
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 59 times.
61x if (sock == INVALID_SOCKET)
1144 2x return make_err(::WSAGetLastError());
1145
1146 118x HANDLE result = ::CreateIoCompletionPort(
1147 59x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1148
1149
2/2
✓ Branch 8 → 9 taken 9 times.
✓ Branch 8 → 12 taken 50 times.
59x if (result == nullptr)
1150 {
1151 9x DWORD dwError = ::GetLastError();
1152 9x ::closesocket(sock);
1153 9x return make_err(dwError);
1154 }
1155
1156 50x impl.socket_ = sock;
1157 50x return {};
1158 }
1159
1160 inline std::error_code
1161 24x win_local_stream_service::assign_acceptor_socket(
1162 win_local_stream_acceptor_internal& impl, native_handle_type fd)
1163 {
1164 24x SOCKET sock = static_cast<SOCKET>(fd);
1165
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 24 times.
24x if (sock == INVALID_SOCKET)
1166 return make_err(WSAENOTSOCK);
1167
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 22 times.
24x if (sock == impl.socket_)
1168 2x return std::make_error_code(std::errc::invalid_argument);
1169
1170 // SO_PROTOCOL_INFOW works on an unbound socket, unlike getsockname
1171 // (WSAEINVAL until bind names it).
1172 22x WSAPROTOCOL_INFOW proto_info{};
1173 22x int proto_len = sizeof(proto_info);
1174
1/1
✓ Branch 6 → 7 taken 22 times.
22x if (::getsockopt(
1175 sock, SOL_SOCKET, SO_PROTOCOL_INFOW,
1176
2/2
✓ Branch 7 → 8 taken 9 times.
✓ Branch 7 → 10 taken 13 times.
22x reinterpret_cast<char*>(&proto_info), &proto_len) != 0)
1177
1/1
✓ Branch 8 → 9 taken 9 times.
9x return make_err(::WSAGetLastError());
1178
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 12 times.
13x if (proto_info.iAddressFamily != AF_UNIX)
1179 1x return make_err(WSAEAFNOSUPPORT);
1180
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 12 times.
12x if (proto_info.iSocketType != SOCK_STREAM)
1181 return make_err(WSAEPROTOTYPE);
1182
1183 // Associate before releasing the held socket: on IOCP nothing
1184 // shares descriptor state the way the reactor path does, so a
1185 // failed association must not cost the caller their old socket.
1186 24x HANDLE result = ::CreateIoCompletionPort(
1187
1/1
✓ Branch 14 → 15 taken 12 times.
12x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1188
2/2
✓ Branch 15 → 16 taken 9 times.
✓ Branch 15 → 18 taken 3 times.
12x if (result == nullptr)
1189
1/1
✓ Branch 16 → 17 taken 9 times.
9x return make_err(::GetLastError());
1190
1191 3x impl.close_socket();
1192 3x impl.socket_ = sock;
1193
1194 3x sockaddr_storage local{};
1195 3x int local_len = sizeof(local);
1196 3x corosio::local_endpoint lep{};
1197
2/3
✓ Branch 19 → 20 taken 3 times.
✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 22 not taken.
3x if (::getsockname(sock, reinterpret_cast<sockaddr*>(&local), &local_len) ==
1198 0)
1199 3x lep = from_sockaddr_local(local, static_cast<socklen_t>(local_len));
1200 3x impl.set_local_endpoint(lep);
1201
1202 3x return {};
1203 }
1204
1205 inline std::error_code
1206 45x win_local_stream_service::bind_acceptor(
1207 win_local_stream_acceptor_internal& impl, corosio::local_endpoint ep)
1208 {
1209 // Reject abstract sockets on Windows
1210
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 44 times.
45x if (ep.is_abstract())
1211 1x return std::make_error_code(std::errc::operation_not_supported);
1212
1213 44x SOCKET sock = impl.socket_;
1214
1215 44x sockaddr_storage storage{};
1216 44x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1217
1/1
✓ Branch 6 → 7 taken 44 times.
44x if (::bind(
1218 sock, reinterpret_cast<sockaddr*>(&storage),
1219
2/2
✓ Branch 7 → 8 taken 3 times.
✓ Branch 7 → 10 taken 41 times.
44x static_cast<int>(addrlen)) == SOCKET_ERROR)
1220
1/1
✓ Branch 8 → 9 taken 3 times.
3x return make_err(::WSAGetLastError());
1221
1222 41x impl.set_local_endpoint(ep);
1223 41x return {};
1224 }
1225
1226 inline std::error_code
1227 36x win_local_stream_service::listen_acceptor(
1228 win_local_stream_acceptor_internal& impl, int backlog)
1229 {
1230 36x SOCKET sock = impl.socket_;
1231
1232
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 34 times.
36x if (::listen(sock, backlog) == SOCKET_ERROR)
1233 2x return make_err(::WSAGetLastError());
1234
1235 34x return {};
1236 }
1237
1238 } // namespace boost::corosio::detail
1239
1240 #endif // BOOST_COROSIO_HAS_IOCP
1241
1242 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_LOCAL_STREAM_SERVICE_HPP
1243