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

99.6% Lines (249 / 250) 77.6% Branches (83 / 107) 100.0% Functions (30 / 30)
win_tcp_acceptor_service.hpp
f(x) Functions (30)
Function Calls Lines Branches Blocks
boost::corosio::detail::accept_op::accept_op(boost::corosio::detail::win_tcp_acceptor&) :85 1156x 100.0% – 100.0% boost::corosio::detail::acceptor_wait_op::acceptor_wait_op(boost::corosio::detail::win_tcp_acceptor&) :92 1156x 100.0% – 100.0% boost::corosio::detail::accept_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :102 434x 100.0% 50.0% 83.3% boost::corosio::detail::acceptor_wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :112 1x 100.0% 50.0% 90.0% boost::corosio::detail::accept_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :126 3304x 100.0% 77.5% 93.7% boost::corosio::detail::acceptor_wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :226 13x 100.0% 100.0% 83.3% boost::corosio::detail::win_tcp_acceptor::win_tcp_acceptor(boost::corosio::detail::win_tcp_service&) :246 1156x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor::reuse() :254 1586x 100.0% 50.0% 73.1% boost::corosio::detail::win_tcp_acceptor::socket_service() :266 802x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor::native_handle() const :272 14x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor::local_endpoint() const :278 2250x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor::is_open() const :284 20989x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor::set_local_endpoint(boost::corosio::endpoint) :290 2683x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor::accept(boost::capy::continuation&, boost::capy::executor_ref, std::stop_token, std::error_code*, boost::corosio::io_object::implementation**) :296 3304x 100.0% 96.0% 96.6% boost::corosio::detail::win_tcp_acceptor::wait(boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :403 13x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::cancel() :446 11x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor::close_socket() :459 10890x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::release_socket() :483 5x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_acceptor::set_option(int, int, void const*, unsigned long long) :498 2267x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_acceptor::get_option(int, int, void*, unsigned long long*) const :509 2x 100.0% 100.0% 85.7% boost::corosio::detail::win_tcp_acceptor::retire() :521 2742x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::win_tcp_acceptor_service(boost::capy::execution_context&) :528 788x 100.0% 100.0% 62.5% boost::corosio::detail::win_tcp_acceptor_service::construct() :535 2742x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::destroy(boost::corosio::io_object::implementation*) :541 2742x 83.3% 50.0% 80.0% boost::corosio::detail::win_tcp_acceptor_service::close(boost::corosio::io_object::handle&) :551 5029x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::open_acceptor_socket(boost::corosio::tcp_acceptor::implementation&, int, int, int) :557 2705x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::assign_socket(boost::corosio::tcp_acceptor::implementation&, unsigned long long) :565 25x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::bind_acceptor(boost::corosio::tcp_acceptor::implementation&, boost::corosio::endpoint) :573 2687x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::listen_acceptor(boost::corosio::tcp_acceptor::implementation&, int) :581 2665x 100.0% – 100.0% boost::corosio::detail::win_tcp_acceptor_service::shutdown() :589 788x 100.0% – 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #ifndef BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
13 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
14
15 #include <boost/corosio/detail/platform.hpp>
16
17 #if BOOST_COROSIO_HAS_IOCP
18
19 #include <boost/corosio/detail/config.hpp>
20 #include <boost/capy/continuation.hpp>
21 #include <boost/capy/ex/execution_context.hpp>
22
23 #include <boost/corosio/native/detail/iocp/win_dissociate.hpp>
24 #include <boost/corosio/native/detail/iocp/win_tcp_acceptor.hpp>
25 #include <boost/corosio/native/detail/iocp/win_tcp_service.hpp>
26
27 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
28 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
29
30 #include <boost/corosio/native/detail/endpoint_convert.hpp>
31 #include <boost/corosio/native/detail/make_err.hpp>
32 #include <boost/corosio/detail/dispatch_coro.hpp>
33
34 #include <ws2tcpip.h>
35
36 #include <system_error>
37
38 namespace boost::corosio::detail {
39
40 /** IOCP acceptor service wrapping win_tcp_service for acceptor lifecycle.
41
42 Provides io_service + acceptor_service interface for tcp_acceptor
43 on Windows. Delegates to win_tcp_service for actual socket operations
44 and for the acceptor pool itself.
45 */
46 class BOOST_COROSIO_DECL win_tcp_acceptor_service final
47 : public capy::execution_context::service
48 , public io_object::io_service
49 {
50 public:
51 using key_type = win_tcp_acceptor_service;
52
53 explicit win_tcp_acceptor_service(capy::execution_context& ctx);
54
55 io_object::implementation* construct() override;
56
57 void destroy(io_object::implementation* p) override;
58
59 void close(io_object::handle& h) override;
60
61 /** Create the acceptor socket without binding or listening. */
62 std::error_code open_acceptor_socket(
63 tcp_acceptor::implementation& impl, int family, int type, int protocol);
64
65 /** Adopt an existing listening socket. */
66 std::error_code
67 assign_socket(tcp_acceptor::implementation& impl, native_handle_type fd);
68
69 /** Bind an open acceptor to a local endpoint. */
70 std::error_code
71 bind_acceptor(tcp_acceptor::implementation& impl, endpoint ep);
72
73 /** Start listening for incoming connections. */
74 std::error_code
75 listen_acceptor(tcp_acceptor::implementation& impl, int backlog);
76
77 void shutdown() override;
78
79 private:
80 win_tcp_service& svc_;
81 };
82
83 // Operation constructors
84
85 1156x inline accept_op::accept_op(win_tcp_acceptor& acceptor_) noexcept
86 : overlapped_op(&do_complete)
87 1156x , acceptor(acceptor_)
88 {
89 1156x cancel_func_ = &do_cancel_impl;
90 1156x }
91
92 1156x inline acceptor_wait_op::acceptor_wait_op(win_tcp_acceptor& acceptor_) noexcept
93 : overlapped_op(&do_complete)
94 1156x , acceptor(acceptor_)
95 {
96 1156x cancel_func_ = &do_cancel_impl;
97 1156x }
98
99 // Cancellation functions
100
101 inline void
102 434x accept_op::do_cancel_impl(overlapped_op* base) noexcept
103 {
104 434x auto* op = static_cast<accept_op*>(base);
105
1/2
✓ Branch 2 → 3 taken 434 times.
✗ Branch 2 → 7 not taken.
434x if (op->listen_socket != INVALID_SOCKET)
106 {
107
1/2
✓ Branch 3 → 4 taken 434 times.
✗ Branch 3 → 5 not taken.
434x ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
108 }
109 434x }
110
111 inline void
112 1x acceptor_wait_op::do_cancel_impl(overlapped_op* base) noexcept
113 {
114 1x auto* op = static_cast<acceptor_wait_op*>(base);
115 1x op->cancelled.store(true, std::memory_order_release);
116
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
1x if (op->listen_socket != INVALID_SOCKET)
117 {
118
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(op->listen_socket), op);
119 }
120 1x op->acceptor.socket_service().scheduler().cancel_wait(op);
121 1x }
122
123 // accept_op completion handler
124
125 inline void
126 3304x accept_op::do_complete(
127 void* owner,
128 scheduler_op* base,
129 std::uint32_t /*bytes*/,
130 std::uint32_t /*error*/)
131 {
132 3304x auto* op = static_cast<accept_op*>(base);
133
134
2/2
✓ Branch 2 → 3 taken 408 times.
✓ Branch 2 → 12 taken 2896 times.
3304x if (!owner)
135 {
136
1/2
✓ Branch 3 → 4 taken 408 times.
✗ Branch 3 → 6 not taken.
408x if (op->accepted_socket != INVALID_SOCKET)
137 {
138
1/1
✓ Branch 4 → 5 taken 408 times.
408x ::closesocket(op->accepted_socket);
139 408x op->accepted_socket = INVALID_SOCKET;
140 }
141
142
1/2
✓ Branch 6 → 7 taken 408 times.
✗ Branch 6 → 10 not taken.
408x if (op->peer_wrapper)
143 {
144 408x op->acceptor.socket_service().destroy(op->peer_wrapper);
145 408x op->peer_wrapper = nullptr;
146 }
147
148 408x op->destroy_awaiting();
149 408x return;
150 }
151
152 2896x op->stop_cb.reset();
153
154 bool success =
155
4/4
✓ Branch 13 → 14 taken 2503 times.
✓ Branch 13 → 17 taken 393 times.
✓ Branch 15 → 16 taken 2498 times.
✓ Branch 15 → 17 taken 5 times.
2896x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
156
157
1/2
✓ Branch 18 → 19 taken 2896 times.
✗ Branch 18 → 27 not taken.
2896x if (op->ec_out)
158 {
159
2/2
✓ Branch 20 → 21 taken 43 times.
✓ Branch 20 → 23 taken 2853 times.
2896x if (op->cancelled.load(std::memory_order_acquire))
160 43x *op->ec_out = capy::error::canceled;
161
2/2
✓ Branch 23 → 24 taken 355 times.
✓ Branch 23 → 25 taken 2498 times.
2853x else if (op->dwError != 0)
162 355x *op->ec_out = iocp_make_err(op->dwError, /*accept_path=*/true);
163 else
164 2498x *op->ec_out = {};
165 }
166
167
4/6
✓ Branch 27 → 28 taken 2498 times.
✓ Branch 27 → 44 taken 398 times.
✓ Branch 28 → 29 taken 2498 times.
✗ Branch 28 → 44 not taken.
✓ Branch 29 → 30 taken 2498 times.
✗ Branch 29 → 44 not taken.
2896x if (success && op->accepted_socket != INVALID_SOCKET && op->peer_wrapper)
168 {
169 2498x ::setsockopt(
170 op->accepted_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
171
1/1
✓ Branch 30 → 31 taken 2498 times.
2498x reinterpret_cast<char*>(&op->listen_socket), sizeof(SOCKET));
172
173 2498x op->peer_wrapper->set_socket(op->accepted_socket);
174
175 2498x sockaddr_storage local_storage{};
176 2498x int local_len = sizeof(local_storage);
177 2498x sockaddr_storage remote_storage{};
178 2498x int remote_len = sizeof(remote_storage);
179
180 2498x endpoint local_ep, remote_ep;
181
1/1
✓ Branch 32 → 33 taken 2498 times.
2498x if (::getsockname(
182 op->accepted_socket,
183
1/2
✓ Branch 33 → 34 taken 2498 times.
✗ Branch 33 → 36 not taken.
2498x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
184 2498x local_ep = from_sockaddr(local_storage);
185
1/1
✓ Branch 36 → 37 taken 2498 times.
2498x if (::getpeername(
186 op->accepted_socket,
187
1/2
✓ Branch 37 → 38 taken 2498 times.
✗ Branch 37 → 40 not taken.
2498x reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) == 0)
188 2498x remote_ep = from_sockaddr(remote_storage);
189
190 2498x op->peer_wrapper->set_endpoints(local_ep, remote_ep);
191 2498x op->accepted_socket = INVALID_SOCKET;
192
193
1/2
✓ Branch 41 → 42 taken 2498 times.
✗ Branch 41 → 43 not taken.
2498x if (op->impl_out)
194 2498x *op->impl_out = op->peer_wrapper;
195 // Handed off to the caller: don't let a failed early return on
196 // the next accept() see this (now user-owned) pointer again.
197 2498x op->peer_wrapper = nullptr;
198 2498x }
199 else
200 {
201
2/2
✓ Branch 44 → 45 taken 393 times.
✓ Branch 44 → 47 taken 5 times.
398x if (op->accepted_socket != INVALID_SOCKET)
202 {
203
1/1
✓ Branch 45 → 46 taken 393 times.
393x ::closesocket(op->accepted_socket);
204 393x op->accepted_socket = INVALID_SOCKET;
205 }
206
207
2/2
✓ Branch 47 → 48 taken 393 times.
✓ Branch 47 → 51 taken 5 times.
398x if (op->peer_wrapper)
208 {
209 393x op->acceptor.socket_service().destroy(op->peer_wrapper);
210 393x op->peer_wrapper = nullptr;
211 }
212
213
1/2
✓ Branch 51 → 52 taken 398 times.
✗ Branch 51 → 53 not taken.
398x if (op->impl_out)
214 398x *op->impl_out = nullptr;
215 }
216
217 2896x auto saved_ex = op->ex;
218 2896x auto prevent_premature_destruction = std::move(op->object_ref_);
219
220
1/1
✓ Branch 56 → 57 taken 2896 times.
2896x dispatch_coro(saved_ex, *op->cont).resume();
221 2896x }
222
223 // acceptor_wait_op completion handler
224
225 inline void
226 13x acceptor_wait_op::do_complete(
227 void* owner,
228 scheduler_op* base,
229 std::uint32_t /*bytes*/,
230 std::uint32_t /*error*/)
231 {
232 13x auto* op = static_cast<acceptor_wait_op*>(base);
233
234
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 5 taken 11 times.
13x if (!owner)
235 {
236 2x op->destroy_awaiting();
237 2x return;
238 }
239
240 11x auto prevent_premature_destruction = std::move(op->object_ref_);
241
1/1
✓ Branch 7 → 8 taken 11 times.
11x op->invoke_handler();
242 11x }
243
244 // win_tcp_acceptor
245
246 1156x inline win_tcp_acceptor::win_tcp_acceptor(win_tcp_service& svc) noexcept
247 1156x : svc_(svc)
248 1156x , acc_(*this)
249 1156x , wt_(*this)
250 {
251 1156x }
252
253 inline void
254 1586x win_tcp_acceptor::reuse() noexcept
255 {
256
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(socket_ == INVALID_SOCKET);
257
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(family_ == AF_UNSPEC);
258
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(local_endpoint_ == endpoint{});
259
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(!acc_.stop_cb);
260
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(!wt_.stop_cb);
261
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(acc_.peer_wrapper == nullptr);
262
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 1586 times.
1586x BOOST_COROSIO_ASSERT(acc_.accepted_socket == INVALID_SOCKET);
263 1586x }
264
265 inline win_tcp_service&
266 802x win_tcp_acceptor::socket_service() noexcept
267 {
268 802x return svc_;
269 }
270
271 inline native_handle_type
272 14x win_tcp_acceptor::native_handle() const noexcept
273 {
274 14x return static_cast<native_handle_type>(socket_);
275 }
276
277 inline endpoint
278 2250x win_tcp_acceptor::local_endpoint() const noexcept
279 {
280 2250x return local_endpoint_;
281 }
282
283 inline bool
284 20989x win_tcp_acceptor::is_open() const noexcept
285 {
286 20989x return socket_ != INVALID_SOCKET;
287 }
288
289 inline void
290 2683x win_tcp_acceptor::set_local_endpoint(endpoint ep) noexcept
291 {
292 2683x local_endpoint_ = ep;
293 2683x }
294
295 inline std::coroutine_handle<>
296 3304x win_tcp_acceptor::accept(
297 capy::continuation& cont,
298 capy::executor_ref d,
299 std::stop_token token,
300 std::error_code* ec,
301 io_object::implementation** impl_out)
302 {
303 // Before anything is armed, so a throw leaves no reference, stop
304 // callback or work count behind.
305
1/1
✓ Branch 2 → 3 taken 3304 times.
3304x auto& peer_wrapper = static_cast<win_tcp_socket&>(*svc_.construct());
306
307 // Keep this acceptor alive during I/O
308 3304x acc_.object_ref_ = detail::object_ref(this);
309
310 3304x auto& op = acc_;
311 3304x op.reset();
312 // reset() doesn't touch these -- a value left over from a prior
313 // accept() (e.g. the socket already handed to the user via
314 // *impl_out) must not survive into a completion this call fails
315 // before reassigning; do_complete's failure branch would destroy()
316 // whatever stale pointer it finds here.
317 3304x op.peer_wrapper = nullptr;
318 3304x op.accepted_socket = INVALID_SOCKET;
319 3304x op.cont = &cont;
320 3304x op.ex = d;
321 3304x op.ec_out = ec;
322 3304x op.impl_out = impl_out;
323 3304x op.start(token);
324
325 3304x svc_.work_started();
326
327 // Derive AF from the listening socket's cached local endpoint
328 3304x int af = native_family(local_endpoint_.address().family());
329
330 // Create the accepted socket with matching address family
331
1/1
✓ Branch 12 → 13 taken 3304 times.
3304x SOCKET accepted = ::WSASocketW(
332 af, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
333
334
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 20 taken 3303 times.
3304x if (accepted == INVALID_SOCKET)
335 {
336 1x svc_.destroy(&peer_wrapper);
337
1/1
✓ Branch 15 → 16 taken 1 time.
1x svc_.on_completion(&op, ::WSAGetLastError(), 0);
338 1x return std::noop_coroutine();
339 }
340
341
1/1
✓ Branch 21 → 22 taken 3303 times.
3303x HANDLE result = ::CreateIoCompletionPort(
342 3303x reinterpret_cast<HANDLE>(accepted), svc_.native_handle(), key_io, 0);
343
344
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 30 taken 3302 times.
3303x if (result == nullptr)
345 {
346
1/1
✓ Branch 23 → 24 taken 1 time.
1x DWORD err = ::GetLastError();
347
1/1
✓ Branch 24 → 25 taken 1 time.
1x ::closesocket(accepted);
348 1x svc_.destroy(&peer_wrapper);
349 1x svc_.on_completion(&op, err, 0);
350 1x return std::noop_coroutine();
351 }
352
353 // Set up the accept operation
354 3302x op.accepted_socket = accepted;
355 3302x op.peer_wrapper = &peer_wrapper;
356 3302x op.listen_socket = socket_;
357
358 3302x auto accept_ex = svc_.accept_ex();
359
2/2
✓ Branch 31 → 32 taken 1 time.
✓ Branch 31 → 38 taken 3301 times.
3302x if (!accept_ex)
360 {
361
1/1
✓ Branch 32 → 33 taken 1 time.
1x ::closesocket(accepted);
362 1x svc_.destroy(&peer_wrapper);
363 1x op.peer_wrapper = nullptr;
364 1x op.accepted_socket = INVALID_SOCKET;
365 1x svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
366 1x return std::noop_coroutine();
367 }
368
369 // AcceptEx address buffer sizes must match the socket's address family
370
2/2
✓ Branch 38 → 39 taken 9 times.
✓ Branch 38 → 40 taken 3292 times.
3301x DWORD addr_size = static_cast<DWORD>(
371 (af == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) + 16);
372 3301x DWORD bytes_received = 0;
373
374 6602x BOOL ok = accept_ex(
375
1/1
✓ Branch 41 → 42 taken 3301 times.
3301x socket_, accepted, op.addr_buf, 0, addr_size, addr_size,
376 &bytes_received, &op);
377
378
1/2
✓ Branch 42 → 43 taken 3301 times.
✗ Branch 42 → 51 not taken.
3301x if (!ok)
379 {
380
1/1
✓ Branch 43 → 44 taken 3301 times.
3301x DWORD err = ::WSAGetLastError();
381
2/2
✓ Branch 44 → 45 taken 2 times.
✓ Branch 44 → 51 taken 3299 times.
3301x if (err != ERROR_IO_PENDING)
382 {
383
1/1
✓ Branch 45 → 46 taken 2 times.
2x ::closesocket(accepted);
384 2x svc_.destroy(&peer_wrapper);
385 2x op.peer_wrapper = nullptr;
386 2x op.accepted_socket = INVALID_SOCKET;
387 2x svc_.on_completion(&op, err, 0);
388 2x return std::noop_coroutine();
389 }
390 }
391
392 // Must precede on_pending: once it runs another thread may
393 // complete the op and recycle this impl.
394
2/2
✓ Branch 52 → 53 taken 4 times.
✓ Branch 52 → 54 taken 3295 times.
3299x if (op.cancelled.load(std::memory_order_acquire))
395
1/1
✓ Branch 53 → 54 taken 4 times.
4x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
396
397 3299x svc_.on_pending(&op);
398
399 3299x return std::noop_coroutine();
400 }
401
402 inline std::coroutine_handle<>
403 13x win_tcp_acceptor::wait(
404 capy::continuation& cont,
405 capy::executor_ref d,
406 wait_type w,
407 std::stop_token token,
408 std::error_code* ec)
409 {
410 13x wt_.object_ref_ = detail::object_ref(this);
411 13x wt_.listen_socket = socket_;
412
413 13x auto& op = wt_;
414 13x op.reset();
415 13x op.cont = &cont;
416 13x op.ex = d;
417 13x op.ec_out = ec;
418 13x op.bytes_out = nullptr;
419 13x op.start(token);
420
421 13x svc_.work_started();
422
423 // Closed-object contract: complete with bad_file_descriptor
424 // without touching the kernel or the wait reactor.
425
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 13 taken 12 times.
13x if (socket_ == INVALID_SOCKET)
426 {
427 1x svc_.on_completion(&op, WSAEBADF, 0);
428 1x return std::noop_coroutine();
429 }
430
431 // Writability carries no meaning for a listening socket; the
432 // wait fails the same way on every backend.
433
2/2
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 18 taken 10 times.
12x if (w == wait_type::write)
434 {
435 2x svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
436 2x return std::noop_coroutine();
437 }
438
439 // wait_type::read (incoming connection ready) and wait_type::error
440 // on the listen socket route through the auxiliary select reactor.
441 10x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
442 10x return std::noop_coroutine();
443 }
444
445 inline void
446 11x win_tcp_acceptor::cancel() noexcept
447 {
448
1/2
✓ Branch 2 → 3 taken 11 times.
✗ Branch 2 → 4 not taken.
11x if (socket_ != INVALID_SOCKET)
449 {
450 11x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
451 }
452
453 11x acc_.request_cancel();
454 11x wt_.request_cancel();
455 11x svc_.scheduler().cancel_wait(&wt_);
456 11x }
457
458 inline void
459 10890x win_tcp_acceptor::close_socket() noexcept
460 {
461 // Flag the accept op cancelled before closing so a closesocket-delivered
462 // ERROR_NETNAME_DELETED is short-circuited to canceled rather than mapped
463 // to connection_aborted by iocp_make_err (see win_tcp_socket close_socket).
464 10890x acc_.request_cancel();
465 // Tear down any aux-reactor-parked wait op first.
466 10890x wt_.request_cancel();
467 10890x svc_.scheduler().cancel_wait(&wt_);
468
469
2/2
✓ Branch 6 → 7 taken 2693 times.
✓ Branch 6 → 10 taken 8197 times.
10890x if (socket_ != INVALID_SOCKET)
470 {
471 2693x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
472 2693x ::closesocket(socket_);
473 2693x socket_ = INVALID_SOCKET;
474 }
475
476 10890x family_ = AF_UNSPEC;
477
478 // Clear cached endpoint
479 10890x local_endpoint_ = endpoint{};
480 10890x }
481
482 inline native_handle_type
483 5x win_tcp_acceptor::release_socket() noexcept
484 {
485 5x SOCKET s = socket_;
486
1/2
✓ Branch 2 → 3 taken 5 times.
✗ Branch 2 → 7 not taken.
5x if (s != INVALID_SOCKET)
487 {
488 5x cancel();
489 5x dissociate_from_iocp(s);
490 5x socket_ = INVALID_SOCKET;
491 5x family_ = AF_UNSPEC;
492 5x local_endpoint_ = endpoint{};
493 }
494 5x return static_cast<native_handle_type>(s);
495 }
496
497 inline std::error_code
498 2267x win_tcp_acceptor::set_option(
499 int level, int optname, void const* data, std::size_t size) noexcept
500 {
501 2267x if (::setsockopt(
502 socket_, level, optname, reinterpret_cast<char const*>(data),
503
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 2266 times.
2267x static_cast<int>(size)) != 0)
504 1x return make_err(WSAGetLastError());
505 2266x return {};
506 }
507
508 inline std::error_code
509 2x win_tcp_acceptor::get_option(
510 int level, int optname, void* data, std::size_t* size) const noexcept
511 {
512 2x int len = static_cast<int>(*size);
513 4x if (::getsockopt(
514
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 1 time.
2x socket_, level, optname, reinterpret_cast<char*>(data), &len) != 0)
515 1x return make_err(WSAGetLastError());
516 1x *size = static_cast<std::size_t>(len);
517 1x return {};
518 }
519
520 inline void
521 2742x win_tcp_acceptor::retire() noexcept
522 {
523 2742x svc_.acceptor_pool_.recycle(this);
524 2742x }
525
526 // win_tcp_acceptor_service
527
528 788x inline win_tcp_acceptor_service::win_tcp_acceptor_service(
529 788x capy::execution_context& ctx)
530
1/1
✓ Branch 4 → 5 taken 788 times.
788x : svc_(ctx.use_service<win_tcp_service>())
531 {
532 788x }
533
534 inline io_object::implementation*
535 2742x win_tcp_acceptor_service::construct()
536 {
537 2742x return svc_.acquire_acceptor_impl();
538 }
539
540 inline void
541 2742x win_tcp_acceptor_service::destroy(io_object::implementation* p)
542 {
543
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2742 times.
2742x if (!p)
544 ✗ return;
545 2742x auto* a = static_cast<win_tcp_acceptor*>(p);
546 2742x a->close_socket();
547 2742x release(a);
548 }
549
550 inline void
551 5029x win_tcp_acceptor_service::close(io_object::handle& h)
552 {
553 5029x static_cast<win_tcp_acceptor*>(h.get())->close_socket();
554 5029x }
555
556 inline std::error_code
557 2705x win_tcp_acceptor_service::open_acceptor_socket(
558 tcp_acceptor::implementation& impl, int family, int type, int protocol)
559 {
560 2705x auto& acc = static_cast<win_tcp_acceptor&>(impl);
561 2705x return svc_.open_acceptor_socket(acc, family, type, protocol);
562 }
563
564 inline std::error_code
565 25x win_tcp_acceptor_service::assign_socket(
566 tcp_acceptor::implementation& impl, native_handle_type fd)
567 {
568 25x auto& acc = static_cast<win_tcp_acceptor&>(impl);
569 25x return svc_.assign_acceptor_socket(acc, fd);
570 }
571
572 inline std::error_code
573 2687x win_tcp_acceptor_service::bind_acceptor(
574 tcp_acceptor::implementation& impl, endpoint ep)
575 {
576 2687x auto& acc = static_cast<win_tcp_acceptor&>(impl);
577 2687x return svc_.bind_acceptor(acc, ep);
578 }
579
580 inline std::error_code
581 2665x win_tcp_acceptor_service::listen_acceptor(
582 tcp_acceptor::implementation& impl, int backlog)
583 {
584 2665x auto& acc = static_cast<win_tcp_acceptor&>(impl);
585 2665x return svc_.listen_acceptor(acc, backlog);
586 }
587
588 inline void
589 788x win_tcp_acceptor_service::shutdown()
590 {
591 // Socket/acceptor shutdown is handled by win_tcp_service::shutdown()
592 788x }
593
594 } // namespace boost::corosio::detail
595
596 #endif // BOOST_COROSIO_HAS_IOCP
597
598 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_ACCEPTOR_SERVICE_HPP
599