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

86.2% Lines (561/651) 98.9% Functions (86/87) 63.5% Branches (167/263)
include/boost/corosio/native/detail/iocp/win_acceptor_service.hpp
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_ACCEPTOR_SERVICE_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_ACCEPTOR_SERVICE_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_HAS_IOCP
17
18 #include <boost/corosio/detail/config.hpp>
19 #include <boost/corosio/detail/except.hpp>
20 #include <boost/capy/ex/execution_context.hpp>
21
22 #include <boost/corosio/native/detail/iocp/win_acceptor.hpp>
23 #include <boost/corosio/native/detail/iocp/win_sockets.hpp>
24
25 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
26 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
27
28 #include <boost/corosio/detail/endpoint_convert.hpp>
29 #include <boost/corosio/detail/make_err.hpp>
30 #include <boost/corosio/detail/dispatch_coro.hpp>
31
32 #include <Ws2tcpip.h>
33
34 namespace boost::corosio::detail {
35
36 /** IOCP acceptor service wrapping win_sockets for acceptor lifecycle.
37
38 Provides io_service + acceptor_service interface for tcp_acceptor
39 on Windows. Delegates to win_sockets for actual socket operations.
40 */
41 class BOOST_COROSIO_DECL win_acceptor_service final
42 : public capy::execution_context::service
43 , public io_object::io_service
44 {
45 public:
46 using key_type = win_acceptor_service;
47
48 win_acceptor_service(capy::execution_context& ctx, win_sockets& svc);
49
50 io_object::implementation* construct() override;
51
52 void destroy(io_object::implementation* p) override;
53
54 void close(io_object::handle& h) override;
55
56 /** Open, bind, and listen on an acceptor socket. */
57 std::error_code
58 open_acceptor(tcp_acceptor::implementation& impl, endpoint ep, int backlog);
59
60 void shutdown() override;
61
62 private:
63 win_sockets& svc_;
64 };
65
66 // ---------------------------------------------------------------
67 // Inline implementations for all classes
68 // ---------------------------------------------------------------
69
70 // Operation constructors
71
72 3712 inline connect_op::connect_op(win_socket_internal& internal_) noexcept
73 : overlapped_op(&do_complete)
74 3712 , internal(internal_)
75 {
76 3712 cancel_func_ = &do_cancel_impl;
77 3712 }
78
79 3712 inline read_op::read_op(win_socket_internal& internal_) noexcept
80 : overlapped_op(&do_complete)
81 3712 , internal(internal_)
82 {
83 3712 cancel_func_ = &do_cancel_impl;
84 3712 }
85
86 3712 inline write_op::write_op(win_socket_internal& internal_) noexcept
87 : overlapped_op(&do_complete)
88 3712 , internal(internal_)
89 {
90 3712 cancel_func_ = &do_cancel_impl;
91 3712 }
92
93 1158 inline accept_op::accept_op() noexcept : overlapped_op(&do_complete)
94 {
95 1158 cancel_func_ = &do_cancel_impl;
96 1158 }
97
98 // Cancellation functions
99
100 inline void
101 connect_op::do_cancel_impl(overlapped_op* base) noexcept
102 {
103 auto* op = static_cast<connect_op*>(base);
104 if (op->internal.is_open())
105 {
106 ::CancelIoEx(
107 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
108 }
109 }
110
111 inline void
112 772 read_op::do_cancel_impl(overlapped_op* base) noexcept
113 {
114 772 auto* op = static_cast<read_op*>(base);
115 772 op->cancelled.store(true, std::memory_order_release);
116
1/2
✓ Branch 4 → 5 taken 772 times.
✗ Branch 4 → 10 not taken.
772 if (op->internal.is_open())
117 {
118
1/2
✓ Branch 5 → 6 taken 772 times.
✗ Branch 5 → 7 not taken.
1544 ::CancelIoEx(
119 772 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
120 }
121 772 }
122
123 inline void
124 2 write_op::do_cancel_impl(overlapped_op* base) noexcept
125 {
126 2 auto* op = static_cast<write_op*>(base);
127 2 op->cancelled.store(true, std::memory_order_release);
128
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 10 not taken.
2 if (op->internal.is_open())
129 {
130
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
4 ::CancelIoEx(
131 2 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
132 }
133 2 }
134
135 inline void
136 6 accept_op::do_cancel_impl(overlapped_op* base) noexcept
137 {
138 6 auto* op = static_cast<accept_op*>(base);
139
1/2
✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 7 not taken.
6 if (op->listen_socket != INVALID_SOCKET)
140 {
141
1/2
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 5 not taken.
6 ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
142 }
143 6 }
144
145 // accept_op completion handler
146
147 inline void
148 1209 accept_op::do_complete(
149 void* owner,
150 scheduler_op* base,
151 std::uint32_t /*bytes*/,
152 std::uint32_t /*error*/)
153 {
154 1209 auto* op = static_cast<accept_op*>(base);
155
156
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 1209 times.
1209 if (!owner)
157 {
158 if (op->accepted_socket != INVALID_SOCKET)
159 {
160 ::closesocket(op->accepted_socket);
161 op->accepted_socket = INVALID_SOCKET;
162 }
163
164 if (op->peer_wrapper)
165 {
166 op->peer_wrapper->close_internal();
167 op->peer_wrapper = nullptr;
168 }
169
170 op->cleanup_only();
171 op->acceptor_ptr.reset();
172 return;
173 }
174
175 1209 op->stop_cb.reset();
176
177 bool success =
178
3/4
✓ Branch 13 → 14 taken 1201 times.
✓ Branch 13 → 17 taken 8 times.
✓ Branch 15 → 16 taken 1201 times.
✗ Branch 15 → 17 not taken.
1209 (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
179
180
1/2
✓ Branch 18 → 19 taken 1209 times.
✗ Branch 18 → 27 not taken.
1209 if (op->ec_out)
181 {
182
2/2
✓ Branch 20 → 21 taken 7 times.
✓ Branch 20 → 23 taken 1202 times.
1209 if (op->cancelled.load(std::memory_order_acquire))
183 7 *op->ec_out = capy::error::canceled;
184
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 1201 times.
1202 else if (op->dwError != 0)
185 1 *op->ec_out = make_err(op->dwError);
186 else
187 1201 *op->ec_out = {};
188 }
189
190
4/6
✓ Branch 27 → 28 taken 1201 times.
✓ Branch 27 → 48 taken 8 times.
✓ Branch 28 → 29 taken 1201 times.
✗ Branch 28 → 48 not taken.
✓ Branch 29 → 30 taken 1201 times.
✗ Branch 29 → 48 not taken.
1209 if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
191 {
192 1201 ::setsockopt(
193 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
194
1/1
✓ Branch 30 → 31 taken 1201 times.
1201 reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
195
196 1201 op->peer_wrapper->get_internal()->set_socket(op->accepted_socket);
197
198 1201 sockaddr_in local_addr{};
199 1201 int local_len = sizeof(local_addr);
200 1201 sockaddr_in remote_addr{};
201 1201 int remote_len = sizeof(remote_addr);
202
203 1201 endpoint local_ep, remote_ep;
204
1/1
✓ Branch 35 → 36 taken 1201 times.
1201 if (::getsockname(
205 op->accepted_socket, reinterpret_cast<sockaddr*>(&local_addr),
206
2/2
✓ Branch 36 → 37 taken 1200 times.
✓ Branch 36 → 39 taken 1 time.
1201 &local_len) == 0)
207 1200 local_ep = from_sockaddr_in(local_addr);
208
1/1
✓ Branch 39 → 40 taken 1201 times.
1201 if (::getpeername(
209 op->accepted_socket, reinterpret_cast<sockaddr*>(&remote_addr),
210
2/2
✓ Branch 40 → 41 taken 1200 times.
✓ Branch 40 → 43 taken 1 time.
1201 &remote_len) == 0)
211 1200 remote_ep = from_sockaddr_in(remote_addr);
212
213 1201 op->peer_wrapper->get_internal()->set_endpoints(local_ep, remote_ep);
214 1201 op->accepted_socket = INVALID_SOCKET;
215
216
1/2
✓ Branch 45 → 46 taken 1201 times.
✗ Branch 45 → 47 not taken.
1201 if (op->impl_out)
217 1201 *op->impl_out = op->peer_wrapper;
218 1201 }
219 else
220 {
221
1/2
✓ Branch 48 → 49 taken 8 times.
✗ Branch 48 → 51 not taken.
8 if (op->accepted_socket != INVALID_SOCKET)
222 {
223
1/1
✓ Branch 49 → 50 taken 8 times.
8 ::closesocket(op->accepted_socket);
224 8 op->accepted_socket = INVALID_SOCKET;
225 }
226
227
1/2
✓ Branch 51 → 52 taken 8 times.
✗ Branch 51 → 56 not taken.
8 if (op->peer_wrapper)
228 {
229
1/1
✓ Branch 54 → 55 taken 8 times.
8 op->acceptor_ptr->socket_service().destroy(op->peer_wrapper);
230 8 op->peer_wrapper = nullptr;
231 }
232
233
1/2
✓ Branch 56 → 57 taken 8 times.
✗ Branch 56 → 58 not taken.
8 if (op->impl_out)
234 8 *op->impl_out = nullptr;
235 }
236
237 1209 auto saved_h = op->h;
238 1209 auto saved_ex = op->ex;
239 1209 auto prevent_premature_destruction = std::move(op->acceptor_ptr);
240
241
2/2
✓ Branch 60 → 61 taken 1209 times.
✓ Branch 61 → 62 taken 1209 times.
1209 dispatch_coro(saved_ex, saved_h).resume();
242 1209 }
243
244 // connect_op completion handler
245
246 inline void
247 1202 connect_op::do_complete(
248 void* owner,
249 scheduler_op* base,
250 std::uint32_t /*bytes*/,
251 std::uint32_t /*error*/)
252 {
253 1202 auto* op = static_cast<connect_op*>(base);
254
255
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1202 times.
1202 if (!owner)
256 {
257 op->cleanup_only();
258 op->internal_ptr.reset();
259 return;
260 }
261
262 bool success =
263
3/4
✓ Branch 6 → 7 taken 1201 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 1201 times.
✗ Branch 8 → 10 not taken.
1202 (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
264
5/6
✓ Branch 11 → 12 taken 1201 times.
✓ Branch 11 → 15 taken 1 time.
✓ Branch 13 → 14 taken 1201 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 1201 times.
✓ Branch 16 → 27 taken 1 time.
1202 if (success && op->internal.is_open())
265 {
266 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
267
1/1
✓ Branch 18 → 19 taken 1201 times.
1201 ::setsockopt(
268 1201 op->internal.native_handle(), SOL_SOCKET,
269 SO_UPDATE_CONNECT_CONTEXT, nullptr, 0);
270
271 1201 endpoint local_ep;
272 1201 sockaddr_in local_addr{};
273 1201 int local_len = sizeof(local_addr);
274
1/1
✓ Branch 21 → 22 taken 1201 times.
1201 if (::getsockname(
275 1201 op->internal.native_handle(),
276
1/2
✓ Branch 22 → 23 taken 1201 times.
✗ Branch 22 → 25 not taken.
1201 reinterpret_cast<sockaddr*>(&local_addr), &local_len) == 0)
277 1201 local_ep = from_sockaddr_in(local_addr);
278 1201 op->internal.set_endpoints(local_ep, op->target_endpoint);
279 }
280
281 1202 auto prevent_premature_destruction = std::move(op->internal_ptr);
282
1/1
✓ Branch 29 → 30 taken 1202 times.
1202 op->invoke_handler();
283 1202 }
284
285 // read_op completion handler
286
287 inline void
288 236040 read_op::do_complete(
289 void* owner,
290 scheduler_op* base,
291 std::uint32_t /*bytes*/,
292 std::uint32_t /*error*/)
293 {
294 236040 auto* op = static_cast<read_op*>(base);
295
296
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 236040 times.
236040 if (!owner)
297 {
298 op->cleanup_only();
299 op->internal_ptr.reset();
300 return;
301 }
302
303 236040 auto prevent_premature_destruction = std::move(op->internal_ptr);
304
1/1
✓ Branch 8 → 9 taken 236040 times.
236040 op->invoke_handler();
305 236040 }
306
307 // write_op completion handler
308
309 inline void
310 234527 write_op::do_complete(
311 void* owner,
312 scheduler_op* base,
313 std::uint32_t /*bytes*/,
314 std::uint32_t /*error*/)
315 {
316 234527 auto* op = static_cast<write_op*>(base);
317
318
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 234527 times.
234527 if (!owner)
319 {
320 op->cleanup_only();
321 op->internal_ptr.reset();
322 return;
323 }
324
325 234527 auto prevent_premature_destruction = std::move(op->internal_ptr);
326
1/1
✓ Branch 8 → 9 taken 234527 times.
234527 op->invoke_handler();
327 234527 }
328
329 // win_socket_internal
330
331 3712 inline win_socket_internal::win_socket_internal(win_sockets& svc) noexcept
332 3712 : svc_(svc)
333 3712 , conn_(*this)
334 3712 , rd_(*this)
335 7424 , wr_(*this)
336 {
337 3712 }
338
339 3712 inline win_socket_internal::~win_socket_internal()
340 {
341 3712 svc_.unregister_impl(*this);
342 3712 }
343
344 inline SOCKET
345 18477 win_socket_internal::native_handle() const noexcept
346 {
347 18477 return socket_;
348 }
349
350 inline endpoint
351 16 win_socket_internal::local_endpoint() const noexcept
352 {
353 16 return local_endpoint_;
354 }
355
356 inline endpoint
357 16 win_socket_internal::remote_endpoint() const noexcept
358 {
359 16 return remote_endpoint_;
360 }
361
362 inline bool
363 1975 win_socket_internal::is_open() const noexcept
364 {
365 1975 return socket_ != INVALID_SOCKET;
366 }
367
368 inline void
369 1201 win_socket_internal::set_socket(SOCKET s) noexcept
370 {
371 1201 socket_ = s;
372 1201 }
373
374 inline void
375 2402 win_socket_internal::set_endpoints(endpoint local, endpoint remote) noexcept
376 {
377 2402 local_endpoint_ = local;
378 2402 remote_endpoint_ = remote;
379 2402 }
380
381 inline std::coroutine_handle<>
382 1202 win_socket_internal::connect(
383 std::coroutine_handle<> h,
384 capy::executor_ref d,
385 endpoint ep,
386 std::stop_token token,
387 std::error_code* ec)
388 {
389 // Keep internal alive during I/O
390
1/1
✓ Branch 2 → 3 taken 1202 times.
1202 conn_.internal_ptr = shared_from_this();
391
392 1202 auto& op = conn_;
393 1202 op.reset();
394 1202 op.h = h;
395 1202 op.ex = d;
396 1202 op.ec_out = ec;
397 1202 op.target_endpoint = ep;
398 1202 op.start(token);
399
400 1202 svc_.work_started();
401
402 1202 sockaddr_in bind_addr{};
403 1202 bind_addr.sin_family = AF_INET;
404 1202 bind_addr.sin_addr.s_addr = INADDR_ANY;
405 1202 bind_addr.sin_port = 0;
406
407
1/1
✓ Branch 10 → 11 taken 1202 times.
1202 if (::bind(
408 socket_, reinterpret_cast<sockaddr*>(&bind_addr),
409
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 17 taken 1202 times.
1202 sizeof(bind_addr)) == SOCKET_ERROR)
410 {
411 svc_.on_completion(&op, ::WSAGetLastError(), 0);
412 return std::noop_coroutine();
413 }
414
415 1202 auto connect_ex = svc_.connect_ex();
416
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 23 taken 1202 times.
1202 if (!connect_ex)
417 {
418 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
419 return std::noop_coroutine();
420 }
421
422 1202 sockaddr_in addr = detail::to_sockaddr_in(ep);
423
424
1/1
✓ Branch 24 → 25 taken 1202 times.
1202 BOOL result = connect_ex(
425 socket_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr), nullptr, 0,
426 nullptr, &op);
427
428
1/2
✓ Branch 25 → 26 taken 1202 times.
✗ Branch 25 → 32 not taken.
1202 if (!result)
429 {
430
1/1
✓ Branch 26 → 27 taken 1202 times.
1202 DWORD err = ::WSAGetLastError();
431
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 32 taken 1202 times.
1202 if (err != ERROR_IO_PENDING)
432 {
433 svc_.on_completion(&op, err, 0);
434 return std::noop_coroutine();
435 }
436 }
437
438 1202 svc_.on_pending(&op);
439 1202 return std::noop_coroutine();
440 }
441
442 inline std::coroutine_handle<>
443 236040 win_socket_internal::read_some(
444 std::coroutine_handle<> h,
445 capy::executor_ref d,
446 io_buffer_param param,
447 std::stop_token token,
448 std::error_code* ec,
449 std::size_t* bytes_out)
450 {
451 // Keep internal alive during I/O
452
1/1
✓ Branch 2 → 3 taken 236040 times.
236040 rd_.internal_ptr = shared_from_this();
453
454 236040 auto& op = rd_;
455 236040 op.reset();
456 236040 op.is_read_ = true;
457 236040 op.h = h;
458 236040 op.ex = d;
459 236040 op.ec_out = ec;
460 236040 op.bytes_out = bytes_out;
461 236040 op.start(token);
462
463 236040 svc_.work_started();
464
465 // Prepare buffers
466 236040 capy::mutable_buffer bufs[read_op::max_buffers];
467 236040 op.wsabuf_count =
468 236040 static_cast<DWORD>(param.copy_to(bufs, read_op::max_buffers));
469
470 // Handle empty buffer: complete with 0 bytes
471
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 236039 times.
236040 if (op.wsabuf_count == 0)
472 {
473 1 op.empty_buffer = true;
474 1 svc_.on_completion(&op, 0, 0);
475 1 return std::noop_coroutine();
476 }
477
478
2/2
✓ Branch 20 → 17 taken 236039 times.
✓ Branch 20 → 21 taken 236039 times.
472078 for (DWORD i = 0; i < op.wsabuf_count; ++i)
479 {
480 236039 op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
481 236039 op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
482 }
483
484 236039 op.flags = 0;
485
486 472078 int result = ::WSARecv(
487
1/1
✓ Branch 21 → 22 taken 236039 times.
236039 socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op,
488 nullptr);
489
490
2/2
✓ Branch 22 → 23 taken 3362 times.
✓ Branch 22 → 29 taken 232677 times.
236039 if (result == SOCKET_ERROR)
491 {
492
1/1
✓ Branch 23 → 24 taken 3362 times.
3362 DWORD err = ::WSAGetLastError();
493
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 24 → 29 taken 3359 times.
3362 if (err != WSA_IO_PENDING)
494 {
495 3 svc_.on_completion(&op, err, 0);
496 3 return std::noop_coroutine();
497 }
498 }
499
500 236036 svc_.on_pending(&op);
501
502 // Re-check cancellation after I/O is pending
503
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 236036 times.
236036 if (op.cancelled.load(std::memory_order_acquire))
504 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
505
506 236036 return std::noop_coroutine();
507 }
508
509 inline std::coroutine_handle<>
510 234527 win_socket_internal::write_some(
511 std::coroutine_handle<> h,
512 capy::executor_ref d,
513 io_buffer_param param,
514 std::stop_token token,
515 std::error_code* ec,
516 std::size_t* bytes_out)
517 {
518 // Keep internal alive during I/O
519
1/1
✓ Branch 2 → 3 taken 234527 times.
234527 wr_.internal_ptr = shared_from_this();
520
521 234527 auto& op = wr_;
522 234527 op.reset();
523 234527 op.h = h;
524 234527 op.ex = d;
525 234527 op.ec_out = ec;
526 234527 op.bytes_out = bytes_out;
527 234527 op.start(token);
528
529 234527 svc_.work_started();
530
531 // Prepare buffers
532 234527 capy::mutable_buffer bufs[write_op::max_buffers];
533 234527 op.wsabuf_count =
534 234527 static_cast<DWORD>(param.copy_to(bufs, write_op::max_buffers));
535
536 // Handle empty buffer: complete immediately with 0 bytes
537
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 234526 times.
234527 if (op.wsabuf_count == 0)
538 {
539 1 svc_.on_completion(&op, 0, 0);
540 1 return std::noop_coroutine();
541 }
542
543
2/2
✓ Branch 20 → 17 taken 234526 times.
✓ Branch 20 → 21 taken 234526 times.
469052 for (DWORD i = 0; i < op.wsabuf_count; ++i)
544 {
545 234526 op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
546 234526 op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
547 }
548
549 469052 int result = ::WSASend(
550
1/1
✓ Branch 21 → 22 taken 234526 times.
234526 socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
551
552
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 29 taken 234525 times.
234526 if (result == SOCKET_ERROR)
553 {
554
1/1
✓ Branch 23 → 24 taken 1 time.
1 DWORD err = ::WSAGetLastError();
555
1/2
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 29 not taken.
1 if (err != WSA_IO_PENDING)
556 {
557 1 svc_.on_completion(&op, err, 0);
558 1 return std::noop_coroutine();
559 }
560 }
561
562 234525 svc_.on_pending(&op);
563
564 // Re-check cancellation after I/O is pending
565
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 234525 times.
234525 if (op.cancelled.load(std::memory_order_acquire))
566 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
567
568 234525 return std::noop_coroutine();
569 }
570
571 inline void
572 2107 win_socket_internal::cancel() noexcept
573 {
574
1/2
✓ Branch 2 → 3 taken 2107 times.
✗ Branch 2 → 4 not taken.
2107 if (socket_ != INVALID_SOCKET)
575 {
576 2107 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
577 }
578
579 2107 conn_.request_cancel();
580 2107 rd_.request_cancel();
581 2107 wr_.request_cancel();
582 2107 }
583
584 inline void
585 11047 win_socket_internal::close_socket() noexcept
586 {
587
2/2
✓ Branch 2 → 3 taken 2416 times.
✓ Branch 2 → 6 taken 8631 times.
11047 if (socket_ != INVALID_SOCKET)
588 {
589 2416 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
590 2416 ::closesocket(socket_);
591 2416 socket_ = INVALID_SOCKET;
592 }
593
594 // Clear cached endpoints
595 11047 local_endpoint_ = endpoint{};
596 11047 remote_endpoint_ = endpoint{};
597 11047 }
598
599 // win_socket
600
601 3712 inline win_socket::win_socket(
602 3712 std::shared_ptr<win_socket_internal> internal) noexcept
603 3712 : internal_(std::move(internal))
604 {
605 3712 }
606
607 inline void
608 3712 win_socket::close_internal() noexcept
609 {
610
1/2
✓ Branch 3 → 4 taken 3712 times.
✗ Branch 3 → 7 not taken.
3712 if (internal_)
611 {
612 3712 internal_->close_socket();
613 3712 internal_.reset();
614 }
615 3712 }
616
617 inline std::coroutine_handle<>
618 1202 win_socket::connect(
619 std::coroutine_handle<> h,
620 capy::executor_ref d,
621 endpoint ep,
622 std::stop_token token,
623 std::error_code* ec)
624 {
625
1/1
✓ Branch 4 → 5 taken 1202 times.
1202 return internal_->connect(h, d, ep, token, ec);
626 }
627
628 inline std::coroutine_handle<>
629 236040 win_socket::read_some(
630 std::coroutine_handle<> h,
631 capy::executor_ref d,
632 io_buffer_param buf,
633 std::stop_token token,
634 std::error_code* ec,
635 std::size_t* bytes)
636 {
637
1/1
✓ Branch 4 → 5 taken 236040 times.
236040 return internal_->read_some(h, d, buf, token, ec, bytes);
638 }
639
640 inline std::coroutine_handle<>
641 234527 win_socket::write_some(
642 std::coroutine_handle<> h,
643 capy::executor_ref d,
644 io_buffer_param buf,
645 std::stop_token token,
646 std::error_code* ec,
647 std::size_t* bytes)
648 {
649
1/1
✓ Branch 4 → 5 taken 234527 times.
234527 return internal_->write_some(h, d, buf, token, ec, bytes);
650 }
651
652 inline std::error_code
653 3 win_socket::shutdown(tcp_socket::shutdown_type what) noexcept
654 {
655 int how;
656
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 1 time.
✗ Branch 2 → 6 not taken.
3 switch (what)
657 {
658 1 case tcp_socket::shutdown_receive:
659 1 how = SD_RECEIVE;
660 1 break;
661 1 case tcp_socket::shutdown_send:
662 1 how = SD_SEND;
663 1 break;
664 1 case tcp_socket::shutdown_both:
665 1 how = SD_BOTH;
666 1 break;
667 default:
668 return make_err(WSAEINVAL);
669 }
670
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 3 times.
3 if (::shutdown(internal_->native_handle(), how) != 0)
671 return make_err(WSAGetLastError());
672 3 return {};
673 }
674
675 inline native_handle_type
676 13168 win_socket::native_handle() const noexcept
677 {
678 13168 return static_cast<native_handle_type>(internal_->native_handle());
679 }
680
681 inline std::error_code
682 5 win_socket::set_no_delay(bool value) noexcept
683 {
684
2/2
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 4 taken 1 time.
5 BOOL flag = value ? TRUE : FALSE;
685 5 if (::setsockopt(
686 5 internal_->native_handle(), IPPROTO_TCP, TCP_NODELAY,
687
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 5 times.
5 reinterpret_cast<char*>(&flag), sizeof(flag)) != 0)
688 return make_err(WSAGetLastError());
689 5 return {};
690 }
691
692 inline bool
693 5 win_socket::no_delay(std::error_code& ec) const noexcept
694 {
695 5 BOOL flag = FALSE;
696 5 int len = sizeof(flag);
697 5 if (::getsockopt(
698 5 internal_->native_handle(), IPPROTO_TCP, TCP_NODELAY,
699
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 5 times.
5 reinterpret_cast<char*>(&flag), &len) != 0)
700 {
701 ec = make_err(WSAGetLastError());
702 return false;
703 }
704 5 ec = {};
705 5 return flag != FALSE;
706 }
707
708 inline std::error_code
709 4 win_socket::set_keep_alive(bool value) noexcept
710 {
711
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 4 taken 1 time.
4 BOOL flag = value ? TRUE : FALSE;
712 4 if (::setsockopt(
713 4 internal_->native_handle(), SOL_SOCKET, SO_KEEPALIVE,
714
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 4 times.
4 reinterpret_cast<char*>(&flag), sizeof(flag)) != 0)
715 return make_err(WSAGetLastError());
716 4 return {};
717 }
718
719 inline bool
720 4 win_socket::keep_alive(std::error_code& ec) const noexcept
721 {
722 4 BOOL flag = FALSE;
723 4 int len = sizeof(flag);
724 4 if (::getsockopt(
725 4 internal_->native_handle(), SOL_SOCKET, SO_KEEPALIVE,
726
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 4 times.
4 reinterpret_cast<char*>(&flag), &len) != 0)
727 {
728 ec = make_err(WSAGetLastError());
729 return false;
730 }
731 4 ec = {};
732 4 return flag != FALSE;
733 }
734
735 inline std::error_code
736 1 win_socket::set_receive_buffer_size(int size) noexcept
737 {
738 1 if (::setsockopt(
739 1 internal_->native_handle(), SOL_SOCKET, SO_RCVBUF,
740
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1 time.
1 reinterpret_cast<char*>(&size), sizeof(size)) != 0)
741 return make_err(WSAGetLastError());
742 1 return {};
743 }
744
745 inline int
746 3 win_socket::receive_buffer_size(std::error_code& ec) const noexcept
747 {
748 3 int size = 0;
749 3 int len = sizeof(size);
750 3 if (::getsockopt(
751 3 internal_->native_handle(), SOL_SOCKET, SO_RCVBUF,
752
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 3 times.
3 reinterpret_cast<char*>(&size), &len) != 0)
753 {
754 ec = make_err(WSAGetLastError());
755 return 0;
756 }
757 3 ec = {};
758 3 return size;
759 }
760
761 inline std::error_code
762 1 win_socket::set_send_buffer_size(int size) noexcept
763 {
764 1 if (::setsockopt(
765 1 internal_->native_handle(), SOL_SOCKET, SO_SNDBUF,
766
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1 time.
1 reinterpret_cast<char*>(&size), sizeof(size)) != 0)
767 return make_err(WSAGetLastError());
768 1 return {};
769 }
770
771 inline int
772 3 win_socket::send_buffer_size(std::error_code& ec) const noexcept
773 {
774 3 int size = 0;
775 3 int len = sizeof(size);
776 3 if (::getsockopt(
777 3 internal_->native_handle(), SOL_SOCKET, SO_SNDBUF,
778
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 3 times.
3 reinterpret_cast<char*>(&size), &len) != 0)
779 {
780 ec = make_err(WSAGetLastError());
781 return 0;
782 }
783 3 ec = {};
784 3 return size;
785 }
786
787 inline std::error_code
788 2102 win_socket::set_linger(bool enabled, int timeout) noexcept
789 {
790
3/4
✓ Branch 2 → 3 taken 2101 times.
✓ Branch 2 → 4 taken 1 time.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2101 times.
2102 if (timeout < 0 || timeout > 65535)
791 1 return make_err(WSAEINVAL);
792 struct ::linger lg;
793
2/2
✓ Branch 5 → 6 taken 2100 times.
✓ Branch 5 → 7 taken 1 time.
2101 lg.l_onoff = enabled ? 1 : 0;
794 2101 lg.l_linger = static_cast<u_short>(timeout);
795 2101 if (::setsockopt(
796 2101 internal_->native_handle(), SOL_SOCKET, SO_LINGER,
797
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 14 taken 2101 times.
2101 reinterpret_cast<char*>(&lg), sizeof(lg)) != 0)
798 return make_err(WSAGetLastError());
799 2101 return {};
800 }
801
802 inline tcp_socket::linger_options
803 3 win_socket::linger(std::error_code& ec) const noexcept
804 {
805 3 struct ::linger lg{};
806 3 int len = sizeof(lg);
807 3 if (::getsockopt(
808 3 internal_->native_handle(), SOL_SOCKET, SO_LINGER,
809
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 3 times.
3 reinterpret_cast<char*>(&lg), &len) != 0)
810 {
811 ec = make_err(WSAGetLastError());
812 return {};
813 }
814 3 ec = {};
815 3 return {.enabled = lg.l_onoff != 0, .timeout = lg.l_linger};
816 }
817
818 inline endpoint
819 16 win_socket::local_endpoint() const noexcept
820 {
821 16 return internal_->local_endpoint();
822 }
823
824 inline endpoint
825 16 win_socket::remote_endpoint() const noexcept
826 {
827 16 return internal_->remote_endpoint();
828 }
829
830 inline void
831 2107 win_socket::cancel() noexcept
832 {
833 2107 internal_->cancel();
834 2107 }
835
836 inline win_socket_internal*
837 9737 win_socket::get_internal() const noexcept
838 {
839 9737 return internal_.get();
840 }
841
842 // win_sockets
843
844 290 inline win_sockets::win_sockets(capy::execution_context& ctx)
845 580 : sched_(ctx.use_service<win_scheduler>())
846
2/2
✓ Branch 5 → 6 taken 290 times.
✓ Branch 6 → 7 taken 290 times.
290 , iocp_(sched_.native_handle())
847 {
848
1/1
✓ Branch 12 → 13 taken 290 times.
290 load_extension_functions();
849 290 }
850
851 580 inline win_sockets::~win_sockets()
852 {
853 // Delete wrappers that survived shutdown. This runs after
854 // win_scheduler is destroyed (reverse creation order), so
855 // all coroutine frames and their tcp_socket members are gone.
856
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 290 times.
290 for (auto* w = socket_wrapper_list_.pop_front(); w != nullptr;
857 w = socket_wrapper_list_.pop_front())
858 delete w;
859
860
1/2
✗ Branch 11 → 8 not taken.
✓ Branch 11 → 12 taken 290 times.
290 for (auto* w = acceptor_wrapper_list_.pop_front(); w != nullptr;
861 w = acceptor_wrapper_list_.pop_front())
862 delete w;
863 580 }
864
865 inline void
866 290 win_sockets::shutdown()
867 {
868 290 std::lock_guard<win_mutex> lock(mutex_);
869
870 // Close all sockets to force pending I/O to complete via IOCP.
871 // Wrappers are NOT deleted here - coroutine frames destroyed
872 // during scheduler shutdown may still hold tcp_socket objects
873 // that reference them. Wrapper deletion is deferred to ~win_sockets
874 // after the scheduler has drained all outstanding operations.
875
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 290 times.
290 for (auto* impl = socket_list_.pop_front(); impl != nullptr;
876 impl = socket_list_.pop_front())
877 {
878 impl->close_socket();
879 }
880
881
1/2
✗ Branch 10 → 8 not taken.
✓ Branch 10 → 11 taken 290 times.
290 for (auto* impl = acceptor_list_.pop_front(); impl != nullptr;
882 impl = acceptor_list_.pop_front())
883 {
884 impl->close_socket();
885 }
886 290 }
887
888 inline io_object::implementation*
889 3712 win_sockets::construct()
890 {
891
1/1
✓ Branch 2 → 3 taken 3712 times.
3712 auto internal = std::make_shared<win_socket_internal>(*this);
892
893 {
894 3712 std::lock_guard<win_mutex> lock(mutex_);
895 3712 socket_list_.push_back(internal.get());
896 3712 }
897
898
1/1
✓ Branch 7 → 8 taken 3712 times.
3712 auto* wrapper = new win_socket(std::move(internal));
899
900 {
901 3712 std::lock_guard<win_mutex> lock(mutex_);
902 3712 socket_wrapper_list_.push_back(wrapper);
903 3712 }
904
905 3712 return wrapper;
906 3712 }
907
908 inline void
909 3712 win_sockets::destroy(io_object::implementation* p)
910 {
911
1/2
✓ Branch 2 → 3 taken 3712 times.
✗ Branch 2 → 5 not taken.
3712 if (p)
912 {
913 3712 auto& wrapper = static_cast<win_socket&>(*p);
914 3712 wrapper.close_internal();
915 3712 destroy_impl(wrapper);
916 }
917 3712 }
918
919 inline void
920 6120 win_sockets::close(io_object::handle& h)
921 {
922 6120 auto& wrapper = static_cast<win_socket&>(*h.get());
923 6120 wrapper.get_internal()->close_socket();
924 6120 }
925
926 inline void
927 3712 win_sockets::destroy_impl(win_socket& impl)
928 {
929 {
930 3712 std::lock_guard<win_mutex> lock(mutex_);
931 3712 socket_wrapper_list_.remove(&impl);
932 3712 }
933
1/2
✓ Branch 5 → 6 taken 3712 times.
✗ Branch 5 → 7 not taken.
3712 delete &impl;
934 3712 }
935
936 inline void
937 3712 win_sockets::unregister_impl(win_socket_internal& impl)
938 {
939 3712 std::lock_guard<win_mutex> lock(mutex_);
940 3712 socket_list_.remove(&impl);
941 3712 }
942
943 inline std::error_code
944 1215 win_sockets::open_socket(win_socket_internal& impl)
945 {
946 1215 impl.close_socket();
947
948 1215 SOCKET sock = ::WSASocketW(
949 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
950
951
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1215 times.
1215 if (sock == INVALID_SOCKET)
952 return make_err(::WSAGetLastError());
953
954 2430 HANDLE result = ::CreateIoCompletionPort(
955 1215 reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
956
957
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 1215 times.
1215 if (result == nullptr)
958 {
959 DWORD dwError = ::GetLastError();
960 ::closesocket(sock);
961 return make_err(dwError);
962 }
963
964 1215 impl.socket_ = sock;
965 1215 return {};
966 }
967
968 inline void*
969 1209 win_sockets::native_handle() const noexcept
970 {
971 1209 return iocp_;
972 }
973
974 inline LPFN_CONNECTEX
975 1202 win_sockets::connect_ex() const noexcept
976 {
977 1202 return connect_ex_;
978 }
979
980 inline LPFN_ACCEPTEX
981 1209 win_sockets::accept_ex() const noexcept
982 {
983 1209 return accept_ex_;
984 }
985
986 inline void
987 win_sockets::post(overlapped_op* op)
988 {
989 sched_.post(op);
990 }
991
992 inline void
993 472972 win_sockets::on_pending(overlapped_op* op) noexcept
994 {
995 472972 sched_.on_pending(op);
996 472972 }
997
998 inline void
999 6 win_sockets::on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept
1000 {
1001 6 sched_.on_completion(op, error, bytes);
1002 6 }
1003
1004 inline void
1005 472978 win_sockets::work_started() noexcept
1006 {
1007 472978 sched_.work_started();
1008 472978 }
1009
1010 inline void
1011 win_sockets::work_finished() noexcept
1012 {
1013 sched_.work_finished();
1014 }
1015
1016 inline void
1017 290 win_sockets::load_extension_functions()
1018 {
1019
1/1
✓ Branch 2 → 3 taken 290 times.
290 SOCKET sock = ::WSASocketW(
1020 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1021
1022
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 290 times.
290 if (sock == INVALID_SOCKET)
1023 return;
1024
1025 290 DWORD bytes = 0;
1026
1027 290 GUID connect_ex_guid = WSAID_CONNECTEX;
1028 290 ::WSAIoctl(
1029 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
1030
1/1
✓ Branch 5 → 6 taken 290 times.
290 sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
1031 nullptr, nullptr);
1032
1033 290 GUID accept_ex_guid = WSAID_ACCEPTEX;
1034 290 ::WSAIoctl(
1035 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
1036
1/1
✓ Branch 6 → 7 taken 290 times.
290 sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
1037 nullptr, nullptr);
1038
1039
1/1
✓ Branch 7 → 8 taken 290 times.
290 ::closesocket(sock);
1040 }
1041
1042 inline void
1043 1158 win_sockets::destroy_acceptor_impl(win_acceptor& impl)
1044 {
1045 {
1046 1158 std::lock_guard<win_mutex> lock(mutex_);
1047 1158 acceptor_wrapper_list_.remove(&impl);
1048 1158 }
1049
1/2
✓ Branch 5 → 6 taken 1158 times.
✗ Branch 5 → 7 not taken.
1158 delete &impl;
1050 1158 }
1051
1052 inline void
1053 1158 win_sockets::unregister_acceptor_impl(win_acceptor_internal& impl)
1054 {
1055 1158 std::lock_guard<win_mutex> lock(mutex_);
1056 1158 acceptor_list_.remove(&impl);
1057 1158 }
1058
1059 inline std::error_code
1060 1156 win_sockets::open_acceptor(
1061 win_acceptor_internal& impl, endpoint ep, int backlog)
1062 {
1063 1156 impl.close_socket();
1064
1065
1/1
✓ Branch 3 → 4 taken 1156 times.
1156 SOCKET sock = ::WSASocketW(
1066 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1067
1068
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 1156 times.
1156 if (sock == INVALID_SOCKET)
1069 return make_err(::WSAGetLastError());
1070
1071 // Allow address reuse
1072 1156 int reuse = 1;
1073
1/1
✓ Branch 7 → 8 taken 1156 times.
1156 ::setsockopt(
1074 sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&reuse),
1075 sizeof(reuse));
1076
1077 2312 HANDLE result = ::CreateIoCompletionPort(
1078
1/1
✓ Branch 8 → 9 taken 1156 times.
1156 reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1079
1080
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 taken 1156 times.
1156 if (result == nullptr)
1081 {
1082 DWORD dwError = ::GetLastError();
1083 ::closesocket(sock);
1084 return make_err(dwError);
1085 }
1086
1087 // Bind to endpoint
1088 1156 sockaddr_in addr = detail::to_sockaddr_in(ep);
1089
3/3
✓ Branch 14 → 15 taken 1156 times.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 19 taken 1154 times.
1156 if (::bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) ==
1090 SOCKET_ERROR)
1091 {
1092
1/1
✓ Branch 16 → 17 taken 2 times.
2 DWORD dwError = ::WSAGetLastError();
1093
1/1
✓ Branch 17 → 18 taken 2 times.
2 ::closesocket(sock);
1094 2 return make_err(dwError);
1095 }
1096
1097 // Start listening
1098
2/3
✓ Branch 19 → 20 taken 1154 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 24 taken 1154 times.
1154 if (::listen(sock, backlog) == SOCKET_ERROR)
1099 {
1100 DWORD dwError = ::WSAGetLastError();
1101 ::closesocket(sock);
1102 return make_err(dwError);
1103 }
1104
1105 1154 impl.socket_ = sock;
1106
1107 // Cache the local endpoint (queries OS for ephemeral port if port was 0)
1108 1154 sockaddr_in local_addr{};
1109 1154 int local_len = sizeof(local_addr);
1110
1/1
✓ Branch 24 → 25 taken 1154 times.
1154 if (::getsockname(
1111
1/2
✓ Branch 25 → 26 taken 1154 times.
✗ Branch 25 → 28 not taken.
1154 sock, reinterpret_cast<sockaddr*>(&local_addr), &local_len) == 0)
1112 1154 impl.set_local_endpoint(detail::from_sockaddr_in(local_addr));
1113
1114 1154 return {};
1115 }
1116
1117 // win_acceptor_internal
1118
1119 1158 inline win_acceptor_internal::win_acceptor_internal(win_sockets& svc) noexcept
1120 1158 : svc_(svc)
1121 {
1122 1158 }
1123
1124 1158 inline win_acceptor_internal::~win_acceptor_internal()
1125 {
1126 1158 svc_.unregister_acceptor_impl(*this);
1127 1158 }
1128
1129 inline win_sockets&
1130 8 win_acceptor_internal::socket_service() noexcept
1131 {
1132 8 return svc_;
1133 }
1134
1135 inline SOCKET
1136 win_acceptor_internal::native_handle() const noexcept
1137 {
1138 return socket_;
1139 }
1140
1141 inline endpoint
1142 1137 win_acceptor_internal::local_endpoint() const noexcept
1143 {
1144 1137 return local_endpoint_;
1145 }
1146
1147 inline bool
1148 5817 win_acceptor_internal::is_open() const noexcept
1149 {
1150 5817 return socket_ != INVALID_SOCKET;
1151 }
1152
1153 inline void
1154 1154 win_acceptor_internal::set_local_endpoint(endpoint ep) noexcept
1155 {
1156 1154 local_endpoint_ = ep;
1157 1154 }
1158
1159 inline std::coroutine_handle<>
1160 1209 win_acceptor_internal::accept(
1161 std::coroutine_handle<> h,
1162 capy::executor_ref d,
1163 std::stop_token token,
1164 std::error_code* ec,
1165 io_object::implementation** impl_out)
1166 {
1167 // Keep acceptor internal alive during I/O
1168
1/1
✓ Branch 2 → 3 taken 1209 times.
1209 acc_.acceptor_ptr = shared_from_this();
1169
1170 1209 auto& op = acc_;
1171 1209 op.reset();
1172 1209 op.h = h;
1173 1209 op.ex = d;
1174 1209 op.ec_out = ec;
1175 1209 op.impl_out = impl_out;
1176 1209 op.start(token);
1177
1178 1209 svc_.work_started();
1179
1180 // Create wrapper for the peer socket (service owns it)
1181
1/1
✓ Branch 10 → 11 taken 1209 times.
1209 auto& peer_wrapper = static_cast<win_socket&>(*svc_.construct());
1182
1183 // Create the accepted socket
1184
1/1
✓ Branch 11 → 12 taken 1209 times.
1209 SOCKET accepted = ::WSASocketW(
1185 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1186
1187
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 19 taken 1209 times.
1209 if (accepted == INVALID_SOCKET)
1188 {
1189 svc_.destroy(&peer_wrapper);
1190 svc_.on_completion(&op, ::WSAGetLastError(), 0);
1191 return std::noop_coroutine();
1192 }
1193
1194
1/1
✓ Branch 20 → 21 taken 1209 times.
1209 HANDLE result = ::CreateIoCompletionPort(
1195 1209 reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
1196
1197
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 29 taken 1209 times.
1209 if (result == nullptr)
1198 {
1199 DWORD err = ::GetLastError();
1200 ::closesocket(accepted);
1201 svc_.destroy(&peer_wrapper);
1202 svc_.on_completion(&op, err, 0);
1203 return std::noop_coroutine();
1204 }
1205
1206 // Set up the accept operation
1207 1209 op.accepted_socket = accepted;
1208 1209 op.peer_wrapper = &peer_wrapper;
1209 1209 op.listen_socket = socket_;
1210
1211 1209 auto accept_ex = svc_.accept_ex();
1212
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 37 taken 1209 times.
1209 if (!accept_ex)
1213 {
1214 ::closesocket(accepted);
1215 svc_.destroy(&peer_wrapper);
1216 op.peer_wrapper = nullptr;
1217 op.accepted_socket = INVALID_SOCKET;
1218 svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
1219 return std::noop_coroutine();
1220 }
1221
1222 1209 DWORD bytes_received = 0;
1223
1224 2418 BOOL ok = accept_ex(
1225
1/1
✓ Branch 37 → 38 taken 1209 times.
1209 socket_, accepted, op.addr_buf, 0, sizeof(sockaddr_in) + 16,
1226 sizeof(sockaddr_in) + 16, &bytes_received, &op);
1227
1228
1/2
✓ Branch 38 → 39 taken 1209 times.
✗ Branch 38 → 47 not taken.
1209 if (!ok)
1229 {
1230
1/1
✓ Branch 39 → 40 taken 1209 times.
1209 DWORD err = ::WSAGetLastError();
1231
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 47 taken 1209 times.
1209 if (err != ERROR_IO_PENDING)
1232 {
1233 ::closesocket(accepted);
1234 svc_.destroy(&peer_wrapper);
1235 op.peer_wrapper = nullptr;
1236 op.accepted_socket = INVALID_SOCKET;
1237 svc_.on_completion(&op, err, 0);
1238 return std::noop_coroutine();
1239 }
1240 }
1241
1242 1209 svc_.on_pending(&op);
1243 1209 return std::noop_coroutine();
1244 }
1245
1246 inline void
1247 1 win_acceptor_internal::cancel() noexcept
1248 {
1249
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 4 not taken.
1 if (socket_ != INVALID_SOCKET)
1250 {
1251 1 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1252 }
1253
1254 1 acc_.request_cancel();
1255 1 }
1256
1257 inline void
1258 4626 win_acceptor_internal::close_socket() noexcept
1259 {
1260
2/2
✓ Branch 2 → 3 taken 1154 times.
✓ Branch 2 → 6 taken 3472 times.
4626 if (socket_ != INVALID_SOCKET)
1261 {
1262 1154 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
1263 1154 ::closesocket(socket_);
1264 1154 socket_ = INVALID_SOCKET;
1265 }
1266
1267 // Clear cached endpoint
1268 4626 local_endpoint_ = endpoint{};
1269 4626 }
1270
1271 // win_acceptor
1272
1273 1158 inline win_acceptor::win_acceptor(
1274 1158 std::shared_ptr<win_acceptor_internal> internal) noexcept
1275 1158 : internal_(std::move(internal))
1276 {
1277 1158 }
1278
1279 inline void
1280 1158 win_acceptor::close_internal() noexcept
1281 {
1282
1/2
✓ Branch 3 → 4 taken 1158 times.
✗ Branch 3 → 7 not taken.
1158 if (internal_)
1283 {
1284 1158 internal_->close_socket();
1285 1158 internal_.reset();
1286 }
1287 1158 }
1288
1289 inline std::coroutine_handle<>
1290 1209 win_acceptor::accept(
1291 std::coroutine_handle<> h,
1292 capy::executor_ref d,
1293 std::stop_token token,
1294 std::error_code* ec,
1295 io_object::implementation** impl_out)
1296 {
1297
1/1
✓ Branch 4 → 5 taken 1209 times.
1209 return internal_->accept(h, d, token, ec, impl_out);
1298 }
1299
1300 inline endpoint
1301 1137 win_acceptor::local_endpoint() const noexcept
1302 {
1303 1137 return internal_->local_endpoint();
1304 }
1305
1306 inline bool
1307 5817 win_acceptor::is_open() const noexcept
1308 {
1309
3/4
✓ Branch 3 → 4 taken 5817 times.
✗ Branch 3 → 8 not taken.
✓ Branch 6 → 7 taken 3514 times.
✓ Branch 6 → 8 taken 2303 times.
5817 return internal_ && internal_->is_open();
1310 }
1311
1312 inline void
1313 1 win_acceptor::cancel() noexcept
1314 {
1315 1 internal_->cancel();
1316 1 }
1317
1318 inline win_acceptor_internal*
1319 3468 win_acceptor::get_internal() const noexcept
1320 {
1321 3468 return internal_.get();
1322 }
1323
1324 // win_acceptor_service
1325
1326 290 inline win_acceptor_service::win_acceptor_service(
1327 290 capy::execution_context& ctx, win_sockets& svc)
1328 290 : svc_(svc)
1329 {
1330 (void)ctx;
1331 290 }
1332
1333 inline io_object::implementation*
1334 1158 win_acceptor_service::construct()
1335 {
1336
1/1
✓ Branch 2 → 3 taken 1158 times.
1158 auto internal = std::make_shared<win_acceptor_internal>(svc_);
1337
1338 {
1339 1158 std::lock_guard<win_mutex> lock(svc_.mutex_);
1340 1158 svc_.acceptor_list_.push_back(internal.get());
1341 1158 }
1342
1343
1/1
✓ Branch 7 → 8 taken 1158 times.
1158 auto* wrapper = new win_acceptor(std::move(internal));
1344
1345 {
1346 1158 std::lock_guard<win_mutex> lock(svc_.mutex_);
1347 1158 svc_.acceptor_wrapper_list_.push_back(wrapper);
1348 1158 }
1349
1350 1158 return wrapper;
1351 1158 }
1352
1353 inline void
1354 1158 win_acceptor_service::destroy(io_object::implementation* p)
1355 {
1356
1/2
✓ Branch 2 → 3 taken 1158 times.
✗ Branch 2 → 5 not taken.
1158 if (p)
1357 {
1358 1158 auto& wrapper = static_cast<win_acceptor&>(*p);
1359 1158 wrapper.close_internal();
1360 1158 svc_.destroy_acceptor_impl(wrapper);
1361 }
1362 1158 }
1363
1364 inline void
1365 2312 win_acceptor_service::close(io_object::handle& h)
1366 {
1367 2312 auto& wrapper = static_cast<win_acceptor&>(*h.get());
1368 2312 wrapper.get_internal()->close_socket();
1369 2312 }
1370
1371 inline std::error_code
1372 1156 win_acceptor_service::open_acceptor(
1373 tcp_acceptor::implementation& impl, endpoint ep, int backlog)
1374 {
1375 1156 auto& wrapper = static_cast<win_acceptor&>(impl);
1376 1156 return svc_.open_acceptor(*wrapper.get_internal(), ep, backlog);
1377 }
1378
1379 inline void
1380 290 win_acceptor_service::shutdown()
1381 {
1382 290 }
1383
1384 } // namespace boost::corosio::detail
1385
1386 #endif // BOOST_COROSIO_HAS_IOCP
1387
1388 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_ACCEPTOR_SERVICE_HPP
1389