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

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