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

97.2% Lines (492 / 506) 83.3% Branches (180 / 216) 98.1% Functions (52 / 53)
win_tcp_service.hpp
f(x) Functions (53)
Function Calls Lines Branches Blocks
boost::corosio::detail::win_tcp_service::connect_ex() const :173 3012x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::accept_ex() const :179 3327x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::scheduler() :200 40735x 100.0% – 100.0% boost::corosio::detail::connect_op::connect_op(boost::corosio::detail::win_tcp_socket&) :221 4005x 100.0% – 100.0% boost::corosio::detail::read_op::read_op(boost::corosio::detail::win_tcp_socket&) :228 4005x 100.0% – 100.0% boost::corosio::detail::write_op::write_op(boost::corosio::detail::win_tcp_socket&) :235 4005x 100.0% – 100.0% boost::corosio::detail::wait_op::wait_op(boost::corosio::detail::win_tcp_socket&) :242 4005x 100.0% – 100.0% boost::corosio::detail::connect_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :252 0 0.0% 0.0% 0.0% boost::corosio::detail::read_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :262 1198x 100.0% 75.0% 85.7% boost::corosio::detail::write_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :273 6x 100.0% 75.0% 85.7% boost::corosio::detail::wait_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :284 1x 100.0% 50.0% 88.9% boost::corosio::detail::connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :304 2987x 100.0% 86.7% 88.9% boost::corosio::detail::read_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :344 212082x 100.0% 100.0% 83.3% boost::corosio::detail::write_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :365 210176x 100.0% 100.0% 83.3% boost::corosio::detail::wait_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :386 42x 100.0% 100.0% 86.7% boost::corosio::detail::win_tcp_socket::win_tcp_socket(boost::corosio::detail::win_tcp_service&) :411 4005x 100.0% – 100.0% boost::corosio::detail::win_tcp_socket::retire() :421 9583x 100.0% – 100.0% boost::corosio::detail::win_tcp_socket::reuse() :427 5578x 100.0% 50.0% 76.0% boost::corosio::detail::win_tcp_socket::set_socket(unsigned long long) :442 2498x 100.0% – 100.0% boost::corosio::detail::win_tcp_socket::set_endpoints(boost::corosio::endpoint, boost::corosio::endpoint) :448 5409x 100.0% – 100.0% boost::corosio::detail::win_tcp_socket::connect(boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::endpoint, std::stop_token, std::error_code*) :455 2987x 97.8% 84.2% 93.5% boost::corosio::detail::win_tcp_socket::read_some(boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::detail::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :544 212082x 97.3% 86.7% 97.6% boost::corosio::detail::win_tcp_socket::write_some(boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::detail::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :620 210176x 97.1% 80.0% 97.6% boost::corosio::detail::win_tcp_socket::wait(boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::wait_type, std::stop_token, std::error_code*) :692 42x 96.7% 90.0% 97.1% boost::corosio::detail::win_tcp_socket::cancel() :762 2034x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket::close_socket() :780 27765x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_socket::shutdown(boost::corosio::shutdown_type) :813 13x 87.5% 83.3% 72.7% boost::corosio::detail::win_tcp_socket::native_handle() const :836 25365x 100.0% – 100.0% boost::corosio::detail::win_tcp_socket::release_socket() :842 8x 100.0% 50.0% 100.0% boost::corosio::detail::win_tcp_socket::set_option(int, int, void const*, unsigned long long) :861 3509x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_socket::get_option(int, int, void*, unsigned long long*) const :872 42x 100.0% 100.0% 85.7% boost::corosio::detail::win_tcp_socket::local_endpoint() const :884 29x 100.0% – 100.0% boost::corosio::detail::win_tcp_socket::remote_endpoint() const :890 37x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::win_tcp_service(boost::capy::execution_context&) :897 1017x 100.0% 100.0% 52.6% boost::corosio::detail::win_tcp_service::shutdown() :905 1016x 100.0% 100.0% 80.0% boost::corosio::detail::win_tcp_service::shutdown()::{lambda(boost::corosio::detail::win_tcp_socket*)#1}::operator()(boost::corosio::detail::win_tcp_socket*) const :913 1235x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_service::shutdown()::{lambda(boost::corosio::detail::win_tcp_acceptor*)#1}::operator()(boost::corosio::detail::win_tcp_acceptor*) const :916 414x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::construct() :920 9583x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::destroy(boost::corosio::io_object::implementation*) :926 9583x 83.3% 50.0% 80.0% boost::corosio::detail::win_tcp_service::close(boost::corosio::io_object::handle&) :936 13897x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::open_socket(boost::corosio::detail::win_tcp_socket&, int, int, int) :942 3050x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_service::assign_socket(boost::corosio::detail::win_tcp_socket&, unsigned long long) :977 27x 97.1% 87.5% 96.8% boost::corosio::detail::win_tcp_service::bind_socket(boost::corosio::detail::win_tcp_socket&, boost::corosio::endpoint) :1027 8x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::native_handle() const :1049 3303x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::on_pending(boost::corosio::detail::overlapped_op*) :1061 428036x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1067 535x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::work_started() :1074 428604x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::load_extension_functions() :1086 1017x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_service::acquire_acceptor_impl() :1112 2742x 100.0% – 100.0% boost::corosio::detail::win_tcp_service::open_acceptor_socket(boost::corosio::detail::win_tcp_acceptor&, int, int, int) :1118 2705x 100.0% 100.0% 100.0% boost::corosio::detail::win_tcp_service::assign_acceptor_socket(boost::corosio::detail::win_tcp_acceptor&, unsigned long long) :1153 25x 96.4% 90.5% 96.2% boost::corosio::detail::win_tcp_service::bind_acceptor(boost::corosio::detail::win_tcp_acceptor&, boost::corosio::endpoint) :1197 2687x 100.0% 85.7% 100.0% boost::corosio::detail::win_tcp_service::listen_acceptor(boost::corosio::detail::win_tcp_acceptor&, int) :1219 2665x 100.0% 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 //
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_TCP_SERVICE_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_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/object_pool.hpp>
20 #include <boost/corosio/detail/object_ref.hpp>
21 #include <boost/capy/continuation.hpp>
22 #include <boost/capy/ex/execution_context.hpp>
23 #include <boost/corosio/detail/intrusive.hpp>
24 #include <boost/corosio/native/detail/iocp/win_dissociate.hpp>
25 #include <boost/corosio/native/detail/iocp/win_validate_handle.hpp>
26 #include <boost/corosio/native/detail/iocp/win_wsa_init.hpp>
27 #include <boost/corosio/native/detail/iocp/win_windows.hpp>
28 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
29 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
30
31 #include <boost/corosio/native/detail/iocp/win_tcp_socket.hpp>
32 #include <boost/corosio/native/detail/iocp/win_tcp_acceptor.hpp>
33
34 #include <mswsock.h>
35
36 namespace boost::corosio::detail {
37
38 class win_scheduler;
39 class win_tcp_acceptor;
40 class win_tcp_acceptor_service;
41
42 /** Windows IOCP socket management service.
43
44 This service owns all socket and acceptor implementations and
45 coordinates their lifecycle with the IOCP. It provides:
46
47 - Socket/acceptor implementation recycling through `object_pool`
48 - IOCP handle association for sockets
49 - Function pointer loading for ConnectEx/AcceptEx
50 - Graceful shutdown - closes every live implementation when the
51 io_context stops; pool destruction frees whatever is left
52
53 @par Thread Safety
54 All public member functions are thread-safe.
55
56 @note Only available on Windows platforms.
57 */
58 class BOOST_COROSIO_DECL win_tcp_service final
59 : private win_wsa_init
60 , public capy::execution_context::service
61 , public io_object::io_service
62 {
63 friend class win_tcp_socket;
64 friend class win_tcp_acceptor;
65
66 public:
67 using key_type = win_tcp_service;
68
69 io_object::implementation* construct() override;
70
71 void destroy(io_object::implementation* p) override;
72
73 void close(io_object::handle& h) override;
74
75 /** Construct the socket service.
76
77 Obtains the IOCP handle from the scheduler service and
78 loads extension function pointers.
79
80 @param ctx Reference to the owning execution_context.
81 */
82 explicit win_tcp_service(capy::execution_context& ctx);
83
84 win_tcp_service(win_tcp_service const&) = delete;
85 win_tcp_service& operator=(win_tcp_service const&) = delete;
86
87 /** Shut down the service. */
88 void shutdown() override;
89
90 /** Create and register a socket with the IOCP.
91
92 @param impl The socket implementation to initialize.
93 @return Error code, or success.
94 */
95 std::error_code open_socket(
96 win_tcp_socket& impl, int family, int type, int protocol);
97
98 /** Adopt an existing socket handle into an implementation.
99
100 Validates family and type before touching the held socket,
101 then associates the new socket with the IOCP. On success the
102 impl takes ownership and will close the handle; on failure
103 the caller retains ownership.
104
105 @param impl The socket implementation to assign to.
106 @param fd The native socket handle to adopt.
107 @return Error code, or success.
108 */
109 std::error_code assign_socket(win_tcp_socket& impl, native_handle_type fd);
110
111 /** Bind a stream socket to a local endpoint.
112
113 @param impl The socket implementation to bind.
114 @param ep The local endpoint to bind to.
115 @return Error code, or success.
116 */
117 std::error_code bind_socket(win_tcp_socket& impl, endpoint ep);
118
119 /** Pop a recycled acceptor or news one, with the service reference
120 held. Used by `win_tcp_acceptor_service::construct()`.
121 */
122 win_tcp_acceptor* acquire_acceptor_impl();
123
124 /** Create an acceptor socket without binding or listening.
125
126 Creates a socket and associates it with the IOCP.
127 For IPv6, dual-stack is enabled by default.
128 Does not set SO_REUSEADDR.
129
130 @param impl The acceptor implementation to initialize.
131 @param family Address family (e.g. `AF_INET`, `AF_INET6`).
132 @param type Socket type (e.g. `SOCK_STREAM`).
133 @param protocol Protocol number (e.g. `IPPROTO_TCP`).
134 @return Error code, or success.
135 */
136 std::error_code open_acceptor_socket(
137 win_tcp_acceptor& impl, int family, int type, int protocol);
138
139 /** Adopt an existing listening socket into an acceptor.
140
141 Validates the socket, associates it with the IOCP, and only
142 then releases the socket the acceptor already held. Listen
143 state is not verified.
144
145 @param impl The acceptor implementation.
146 @param fd The native socket to adopt. Ownership transfers only
147 on success.
148 @return Error code, or success.
149 */
150 std::error_code
151 assign_acceptor_socket(win_tcp_acceptor& impl, native_handle_type fd);
152
153 /** Bind an open acceptor to a local endpoint.
154
155 @param impl The acceptor implementation.
156 @param ep The local endpoint to bind to.
157 @return Error code, or success.
158 */
159 std::error_code bind_acceptor(win_tcp_acceptor& impl, endpoint ep);
160
161 /** Start listening for incoming connections.
162
163 @param impl The acceptor implementation.
164 @param backlog The listen backlog.
165 @return Error code, or success.
166 */
167 std::error_code listen_acceptor(win_tcp_acceptor& impl, int backlog);
168
169 /** Return the IOCP handle. */
170 void* native_handle() const noexcept;
171
172 /** Return the ConnectEx function pointer. */
173 3012x LPFN_CONNECTEX connect_ex() const noexcept
174 {
175 3012x return connect_ex_;
176 }
177
178 /** Return the AcceptEx function pointer. */
179 3327x LPFN_ACCEPTEX accept_ex() const noexcept
180 {
181 3327x return accept_ex_;
182 }
183
184 /** Post an overlapped operation for completion. */
185 void post(overlapped_op* op);
186
187 /** Signal that an overlapped I/O is now pending (CAS protocol). */
188 void on_pending(overlapped_op* op) noexcept;
189
190 /** Post an immediate completion with pre-stored results. */
191 void on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept;
192
193 /** Notify scheduler of pending I/O work. */
194 void work_started() noexcept;
195
196 /** Notify scheduler that I/O work completed. */
197 void work_finished() noexcept;
198
199 /** Return the owning IOCP scheduler. */
200 40735x win_scheduler& scheduler() noexcept
201 {
202 40735x return sched_;
203 }
204
205 private:
206 void load_extension_functions();
207
208 win_scheduler& sched_;
209 void* iocp_;
210 LPFN_CONNECTEX connect_ex_ = nullptr;
211 LPFN_ACCEPTEX accept_ex_ = nullptr;
212 BOOST_COROSIO_MSVC_WARNING_PUSH
213 BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) // detail:: members, dll-interface
214 object_pool<win_tcp_socket> pool_;
215 object_pool<win_tcp_acceptor> acceptor_pool_;
216 BOOST_COROSIO_MSVC_WARNING_POP
217 };
218
219 // Operation constructors
220
221 4005x inline connect_op::connect_op(win_tcp_socket& internal_) noexcept
222 : overlapped_op(&do_complete)
223 4005x , internal(internal_)
224 {
225 4005x cancel_func_ = &do_cancel_impl;
226 4005x }
227
228 4005x inline read_op::read_op(win_tcp_socket& internal_) noexcept
229 : overlapped_op(&do_complete)
230 4005x , internal(internal_)
231 {
232 4005x cancel_func_ = &do_cancel_impl;
233 4005x }
234
235 4005x inline write_op::write_op(win_tcp_socket& internal_) noexcept
236 : overlapped_op(&do_complete)
237 4005x , internal(internal_)
238 {
239 4005x cancel_func_ = &do_cancel_impl;
240 4005x }
241
242 4005x inline wait_op::wait_op(win_tcp_socket& internal_) noexcept
243 : overlapped_op(&do_complete)
244 4005x , internal(internal_)
245 {
246 4005x cancel_func_ = &do_cancel_impl;
247 4005x }
248
249 // Cancellation functions
250
251 inline void
252 ✗ connect_op::do_cancel_impl(overlapped_op* base) noexcept
253 {
254 ✗ auto* op = static_cast<connect_op*>(base);
255
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 7 not taken.
✗ if (op->internal.socket_ != INVALID_SOCKET)
256 {
257
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
✗ ::CancelIoEx(reinterpret_cast<HANDLE>(op->internal.socket_), op);
258 }
259 ✗ }
260
261 inline void
262 1198x read_op::do_cancel_impl(overlapped_op* base) noexcept
263 {
264 1198x auto* op = static_cast<read_op*>(base);
265 1198x op->cancelled.store(true, std::memory_order_release);
266
2/2
✓ Branch 3 → 4 taken 1184 times.
✓ Branch 3 → 8 taken 14 times.
1198x if (op->internal.socket_ != INVALID_SOCKET)
267 {
268
1/2
✓ Branch 4 → 5 taken 1184 times.
✗ Branch 4 → 6 not taken.
1184x ::CancelIoEx(reinterpret_cast<HANDLE>(op->internal.socket_), op);
269 }
270 1198x }
271
272 inline void
273 6x write_op::do_cancel_impl(overlapped_op* base) noexcept
274 {
275 6x auto* op = static_cast<write_op*>(base);
276 6x op->cancelled.store(true, std::memory_order_release);
277
2/2
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 8 taken 1 time.
6x if (op->internal.socket_ != INVALID_SOCKET)
278 {
279
1/2
✓ Branch 4 → 5 taken 5 times.
✗ Branch 4 → 6 not taken.
5x ::CancelIoEx(reinterpret_cast<HANDLE>(op->internal.socket_), op);
280 }
281 6x }
282
283 inline void
284 1x wait_op::do_cancel_impl(overlapped_op* base) noexcept
285 {
286 1x auto* op = static_cast<wait_op*>(base);
287 1x op->cancelled.store(true, std::memory_order_release);
288 // Best-effort cancel of any pending zero-byte WSARecv issued for
289 // wait_type::read. ERROR_NOT_FOUND when nothing overlapped is
290 // pending is harmless.
291
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
1x if (op->internal.socket_ != INVALID_SOCKET)
292 {
293
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(op->internal.socket_), op);
294 }
295 // wait_type::error parks the op in the auxiliary select reactor;
296 // wake it so the reactor can post a cancelled completion. A cancel
297 // for an op the reactor never registered finds nothing and returns.
298 1x op->internal.svc_.scheduler().cancel_wait(op);
299 1x }
300
301 // connect_op completion handler
302
303 inline void
304 2987x connect_op::do_complete(
305 void* owner,
306 scheduler_op* base,
307 std::uint32_t /*bytes*/,
308 std::uint32_t /*error*/)
309 {
310 2987x auto* op = static_cast<connect_op*>(base);
311
312
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 5 taken 2986 times.
2987x if (!owner)
313 {
314 1x op->destroy_awaiting();
315 1x return;
316 }
317
318 bool success =
319
4/4
✓ Branch 5 → 6 taken 2918 times.
✓ Branch 5 → 9 taken 68 times.
✓ Branch 7 → 8 taken 2908 times.
✓ Branch 7 → 9 taken 10 times.
2986x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
320
3/4
✓ Branch 10 → 11 taken 2908 times.
✓ Branch 10 → 19 taken 78 times.
✓ Branch 11 → 12 taken 2908 times.
✗ Branch 11 → 19 not taken.
2986x if (success && op->internal.socket_ != INVALID_SOCKET)
321 {
322 // Required after ConnectEx to enable shutdown(), getsockname(), etc.
323 2908x ::setsockopt(
324
1/1
✓ Branch 12 → 13 taken 2908 times.
2908x op->internal.socket_, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT,
325 nullptr, 0);
326
327 2908x endpoint local_ep;
328 2908x sockaddr_storage local_storage{};
329 2908x int local_len = sizeof(local_storage);
330 5816x if (::getsockname(
331
1/1
✓ Branch 13 → 14 taken 2908 times.
2908x op->internal.socket_,
332
1/2
✓ Branch 14 → 15 taken 2908 times.
✗ Branch 14 → 17 not taken.
2908x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
333 2908x local_ep = from_sockaddr(local_storage);
334 2908x op->internal.set_endpoints(local_ep, op->target_endpoint);
335 }
336
337 2986x auto prevent_premature_destruction = std::move(op->object_ref_);
338
1/1
✓ Branch 21 → 22 taken 2986 times.
2986x op->invoke_handler();
339 2986x }
340
341 // read_op completion handler
342
343 inline void
344 212082x read_op::do_complete(
345 void* owner,
346 scheduler_op* base,
347 std::uint32_t /*bytes*/,
348 std::uint32_t /*error*/)
349 {
350 212082x auto* op = static_cast<read_op*>(base);
351
352
2/2
✓ Branch 2 → 3 taken 211 times.
✓ Branch 2 → 5 taken 211871 times.
212082x if (!owner)
353 {
354 211x op->destroy_awaiting();
355 211x return;
356 }
357
358 211871x auto prevent_premature_destruction = std::move(op->object_ref_);
359
1/1
✓ Branch 7 → 8 taken 211871 times.
211871x op->invoke_handler();
360 211871x }
361
362 // write_op completion handler
363
364 inline void
365 210176x write_op::do_complete(
366 void* owner,
367 scheduler_op* base,
368 std::uint32_t /*bytes*/,
369 std::uint32_t /*error*/)
370 {
371 210176x auto* op = static_cast<write_op*>(base);
372
373
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 5 taken 210175 times.
210176x if (!owner)
374 {
375 1x op->destroy_awaiting();
376 1x return;
377 }
378
379 210175x auto prevent_premature_destruction = std::move(op->object_ref_);
380
1/1
✓ Branch 7 → 8 taken 210175 times.
210175x op->invoke_handler();
381 210175x }
382
383 // wait_op completion handler
384
385 inline void
386 42x wait_op::do_complete(
387 void* owner,
388 scheduler_op* base,
389 std::uint32_t /*bytes*/,
390 std::uint32_t /*error*/)
391 {
392 42x auto* op = static_cast<wait_op*>(base);
393
394
2/2
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 5 taken 38 times.
42x if (!owner)
395 {
396 4x op->destroy_awaiting();
397 4x return;
398 }
399
400 // Only the zero-byte WSARecv read wait; write and error waits come
401 // from the poll reactor through this op too.
402
2/2
✓ Branch 5 → 6 taken 17 times.
✓ Branch 5 → 8 taken 21 times.
38x if (op->w == wait_type::read)
403 17x op->dwError = normalize_read_wait_error(op->dwError);
404
405 38x auto prevent_premature_destruction = std::move(op->object_ref_);
406
1/1
✓ Branch 10 → 11 taken 38 times.
38x op->invoke_handler();
407 38x }
408
409 // win_tcp_socket
410
411 4005x inline win_tcp_socket::win_tcp_socket(win_tcp_service& svc) noexcept
412 4005x : svc_(svc)
413 4005x , conn_(*this)
414 4005x , rd_(*this)
415 4005x , wr_(*this)
416 8010x , wt_(*this)
417 {
418 4005x }
419
420 inline void
421 9583x win_tcp_socket::retire() noexcept
422 {
423 9583x svc_.pool_.recycle(this);
424 9583x }
425
426 inline void
427 5578x win_tcp_socket::reuse() noexcept
428 {
429
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5578 times.
5578x BOOST_COROSIO_ASSERT(socket_ == INVALID_SOCKET);
430
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 5578 times.
5578x BOOST_COROSIO_ASSERT(family_ == AF_UNSPEC);
431 // A connect completing on another thread while the socket closed
432 // can still have written these.
433 5578x local_endpoint_ = endpoint{};
434 5578x remote_endpoint_ = endpoint{};
435
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 5578 times.
5578x BOOST_COROSIO_ASSERT(!conn_.stop_cb);
436
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 5578 times.
5578x BOOST_COROSIO_ASSERT(!rd_.stop_cb);
437
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 5578 times.
5578x BOOST_COROSIO_ASSERT(!wr_.stop_cb);
438
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 5578 times.
5578x BOOST_COROSIO_ASSERT(!wt_.stop_cb);
439 5578x }
440
441 inline void
442 2498x win_tcp_socket::set_socket(SOCKET s) noexcept
443 {
444 2498x socket_ = s;
445 2498x }
446
447 inline void
448 5409x win_tcp_socket::set_endpoints(endpoint local, endpoint remote) noexcept
449 {
450 5409x local_endpoint_ = local;
451 5409x remote_endpoint_ = remote;
452 5409x }
453
454 inline std::coroutine_handle<>
455 2987x win_tcp_socket::connect(
456 capy::continuation& cont,
457 capy::executor_ref d,
458 endpoint ep,
459 std::stop_token token,
460 std::error_code* ec)
461 {
462 // Keep this socket alive during I/O
463 2987x conn_.object_ref_ = detail::object_ref(this);
464
465 2987x auto& op = conn_;
466 2987x op.reset();
467 2987x op.cont = &cont;
468 2987x op.ex = d;
469 2987x op.ec_out = ec;
470 2987x op.target_endpoint = ep;
471 2987x op.start(token);
472
473 2987x svc_.work_started();
474
475 // ConnectEx requires the socket to be bound. Skip if already bound
476 // (e.g. the caller used tcp_socket::bind() before connect).
477
2/2
✓ Branch 10 → 11 taken 2986 times.
✓ Branch 10 → 22 taken 1 time.
2987x if (local_endpoint_ == endpoint{})
478 {
479 2986x sockaddr_storage bind_storage{};
480 socklen_t bind_len;
481
2/2
✓ Branch 11 → 12 taken 7 times.
✓ Branch 11 → 13 taken 2979 times.
2986x if (family_ == AF_INET6)
482 {
483 7x sockaddr_in6 sa6{};
484 7x sa6.sin6_family = AF_INET6;
485 7x sa6.sin6_port = 0;
486 7x sa6.sin6_addr = in6addr_any;
487 7x std::memcpy(&bind_storage, &sa6, sizeof(sa6));
488 7x bind_len = sizeof(sa6);
489 }
490 else
491 {
492 2979x sockaddr_in sa4{};
493 2979x sa4.sin_family = AF_INET;
494 2979x sa4.sin_addr.s_addr = INADDR_ANY;
495 2979x sa4.sin_port = 0;
496 2979x std::memcpy(&bind_storage, &sa4, sizeof(sa4));
497 2979x bind_len = sizeof(sa4);
498 }
499
500
1/1
✓ Branch 14 → 15 taken 2986 times.
2986x if (::bind(
501 socket_, reinterpret_cast<sockaddr*>(&bind_storage),
502
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 21 taken 2985 times.
2986x bind_len) == SOCKET_ERROR)
503 {
504
1/1
✓ Branch 16 → 17 taken 1 time.
1x svc_.on_completion(&op, ::WSAGetLastError(), 0);
505 1x return std::noop_coroutine();
506 }
507 }
508
509 2986x auto connect_ex = svc_.connect_ex();
510
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 28 taken 2985 times.
2986x if (!connect_ex)
511 {
512 1x svc_.on_completion(&op, WSAEOPNOTSUPP, 0);
513 1x return std::noop_coroutine();
514 }
515
516 2985x sockaddr_storage storage{};
517 2985x socklen_t addrlen = detail::to_sockaddr(ep, family_, storage);
518
519
1/1
✓ Branch 29 → 30 taken 2985 times.
2985x BOOL result = connect_ex(
520 socket_, reinterpret_cast<sockaddr*>(&storage),
521 static_cast<int>(addrlen), nullptr, 0, nullptr, &op);
522
523
1/2
✓ Branch 30 → 31 taken 2985 times.
✗ Branch 30 → 37 not taken.
2985x if (!result)
524 {
525
1/1
✓ Branch 31 → 32 taken 2985 times.
2985x DWORD err = ::WSAGetLastError();
526
2/2
✓ Branch 32 → 33 taken 1 time.
✓ Branch 32 → 37 taken 2984 times.
2985x if (err != ERROR_IO_PENDING)
527 {
528 1x svc_.on_completion(&op, err, 0);
529 1x return std::noop_coroutine();
530 }
531 }
532
533 // A stop before ConnectEx was issued found nothing to cancel. Must
534 // precede on_pending: once it runs another thread may complete the
535 // op and recycle this impl.
536
1/2
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 2984 times.
2984x if (op.cancelled.load(std::memory_order_acquire))
537
0/1
✗ Branch 39 → 40 not taken.
✗ ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
538
539 2984x svc_.on_pending(&op);
540 2984x return std::noop_coroutine();
541 }
542
543 inline std::coroutine_handle<>
544 212082x win_tcp_socket::read_some(
545 capy::continuation& cont,
546 capy::executor_ref d,
547 buffer_param param,
548 std::stop_token token,
549 std::error_code* ec,
550 std::size_t* bytes_out)
551 {
552 // Keep this socket alive during I/O
553 212082x rd_.object_ref_ = detail::object_ref(this);
554
555 212082x auto& op = rd_;
556 212082x op.reset();
557 212082x op.is_read = true;
558 212082x op.cont = &cont;
559 212082x op.ex = d;
560 212082x op.ec_out = ec;
561 212082x op.bytes_out = bytes_out;
562 212082x op.start(token);
563
564 212082x svc_.work_started();
565
566 // Closed-object contract: complete with bad_file_descriptor
567 // without touching the kernel.
568
2/2
✓ Branch 8 → 9 taken 151 times.
✓ Branch 8 → 13 taken 211931 times.
212082x if (socket_ == INVALID_SOCKET)
569 {
570 151x svc_.on_completion(&op, WSAEBADF, 0);
571 151x return std::noop_coroutine();
572 }
573
574 // Prepare buffers
575 211931x capy::mutable_buffer bufs[read_op::max_buffers];
576 211931x op.wsabuf_count =
577 211931x static_cast<DWORD>(param.copy_to(bufs, read_op::max_buffers));
578
579 // Handle empty buffer: complete with 0 bytes
580
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 19 taken 211930 times.
211931x if (op.wsabuf_count == 0)
581 {
582 1x op.empty_buffer = true;
583 1x svc_.on_completion(&op, 0, 0);
584 1x return std::noop_coroutine();
585 }
586
587
2/2
✓ Branch 24 → 20 taken 211933 times.
✓ Branch 24 → 25 taken 211930 times.
423863x for (DWORD i = 0; i < op.wsabuf_count; ++i)
588 {
589 211933x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
590 211933x op.wsabufs[i].len = static_cast<ULONG>(
591 211933x (std::min)(bufs[i].size(), std::size_t(0x7fffffff)));
592 }
593
594 211930x op.flags = 0;
595
596 423860x int result = ::WSARecv(
597
1/1
✓ Branch 25 → 26 taken 211930 times.
211930x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
598
599
2/2
✓ Branch 26 → 27 taken 4263 times.
✓ Branch 26 → 33 taken 207667 times.
211930x if (result == SOCKET_ERROR)
600 {
601
1/1
✓ Branch 27 → 28 taken 4263 times.
4263x DWORD err = ::WSAGetLastError();
602
2/2
✓ Branch 28 → 29 taken 137 times.
✓ Branch 28 → 33 taken 4126 times.
4263x if (err != WSA_IO_PENDING)
603 {
604 137x svc_.on_completion(&op, err, 0);
605 137x return std::noop_coroutine();
606 }
607 }
608
609 // Must precede on_pending: once it runs another thread may
610 // complete the op and recycle this impl.
611
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 211793 times.
211793x if (op.cancelled.load(std::memory_order_acquire))
612
0/1
✗ Branch 35 → 36 not taken.
✗ ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
613
614 211793x svc_.on_pending(&op);
615
616 211793x return std::noop_coroutine();
617 }
618
619 inline std::coroutine_handle<>
620 210176x win_tcp_socket::write_some(
621 capy::continuation& cont,
622 capy::executor_ref d,
623 buffer_param param,
624 std::stop_token token,
625 std::error_code* ec,
626 std::size_t* bytes_out)
627 {
628 // Keep this socket alive during I/O
629 210176x wr_.object_ref_ = detail::object_ref(this);
630
631 210176x auto& op = wr_;
632 210176x op.reset();
633 210176x op.cont = &cont;
634 210176x op.ex = d;
635 210176x op.ec_out = ec;
636 210176x op.bytes_out = bytes_out;
637 210176x op.start(token);
638
639 210176x svc_.work_started();
640
641 // Closed-object contract: complete with bad_file_descriptor
642 // without touching the kernel.
643
2/2
✓ Branch 8 → 9 taken 85 times.
✓ Branch 8 → 13 taken 210091 times.
210176x if (socket_ == INVALID_SOCKET)
644 {
645 85x svc_.on_completion(&op, WSAEBADF, 0);
646 85x return std::noop_coroutine();
647 }
648
649 // Prepare buffers
650 210091x capy::mutable_buffer bufs[write_op::max_buffers];
651 210091x op.wsabuf_count =
652 210091x static_cast<DWORD>(param.copy_to(bufs, write_op::max_buffers));
653
654 // Handle empty buffer: complete immediately with 0 bytes
655
2/2
✓ Branch 14 → 15 taken 141 times.
✓ Branch 14 → 19 taken 209950 times.
210091x if (op.wsabuf_count == 0)
656 {
657 141x svc_.on_completion(&op, 0, 0);
658 141x return std::noop_coroutine();
659 }
660
661
2/2
✓ Branch 24 → 20 taken 209952 times.
✓ Branch 24 → 25 taken 209950 times.
419902x for (DWORD i = 0; i < op.wsabuf_count; ++i)
662 {
663 209952x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
664 209952x op.wsabufs[i].len = static_cast<ULONG>(
665 209952x (std::min)(bufs[i].size(), std::size_t(0x7fffffff)));
666 }
667
668 419900x int result = ::WSASend(
669
1/1
✓ Branch 25 → 26 taken 209950 times.
209950x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
670
671
2/2
✓ Branch 26 → 27 taken 5 times.
✓ Branch 26 → 33 taken 209945 times.
209950x if (result == SOCKET_ERROR)
672 {
673
1/1
✓ Branch 27 → 28 taken 5 times.
5x DWORD err = ::WSAGetLastError();
674
1/2
✓ Branch 28 → 29 taken 5 times.
✗ Branch 28 → 33 not taken.
5x if (err != WSA_IO_PENDING)
675 {
676 5x svc_.on_completion(&op, err, 0);
677 5x return std::noop_coroutine();
678 }
679 }
680
681 // Must precede on_pending: once it runs another thread may
682 // complete the op and recycle this impl.
683
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 209945 times.
209945x if (op.cancelled.load(std::memory_order_acquire))
684
0/1
✗ Branch 35 → 36 not taken.
✗ ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
685
686 209945x svc_.on_pending(&op);
687
688 209945x return std::noop_coroutine();
689 }
690
691 inline std::coroutine_handle<>
692 42x win_tcp_socket::wait(
693 capy::continuation& cont,
694 capy::executor_ref d,
695 wait_type w,
696 std::stop_token token,
697 std::error_code* ec)
698 {
699 42x wt_.object_ref_ = detail::object_ref(this);
700
701 42x auto& op = wt_;
702 42x op.reset();
703 42x op.cont = &cont;
704 42x op.ex = d;
705 42x op.ec_out = ec;
706 42x op.bytes_out = nullptr;
707 42x op.empty_buffer = true; // skip EOF translation in invoke_handler
708 42x op.w = w;
709 42x op.start(token);
710
711 42x svc_.work_started();
712
713 // Closed-object contract: complete with bad_file_descriptor
714 // without touching the kernel or the wait reactor.
715
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 40 times.
42x if (socket_ == INVALID_SOCKET)
716 {
717 2x svc_.on_completion(&op, WSAEBADF, 0);
718 2x return std::noop_coroutine();
719 }
720
721
2/2
✓ Branch 13 → 14 taken 17 times.
✓ Branch 13 → 29 taken 23 times.
40x if (w == wait_type::read)
722 {
723 // Zero-byte WSARecv: kernel signals completion when data is
724 // available without consuming any bytes from the stream. This
725 // is the documented Winsock pattern for "is the socket
726 // readable" notifications.
727 17x op.wsabuf = WSABUF{0, nullptr};
728 17x op.flags = 0;
729
730 int result =
731 17x ::WSARecv(socket_, &op.wsabuf, 1, nullptr, &op.flags, &op, nullptr);
732
733
2/2
✓ Branch 15 → 16 taken 12 times.
✓ Branch 15 → 22 taken 5 times.
17x if (result == SOCKET_ERROR)
734 {
735 12x DWORD err = ::WSAGetLastError();
736
2/2
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 22 taken 10 times.
12x if (err != WSA_IO_PENDING)
737 {
738 2x svc_.on_completion(&op, err, 0);
739 2x return std::noop_coroutine();
740 }
741 }
742
743 // Must precede on_pending: once it runs another thread may
744 // complete the op and recycle this impl.
745
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 15 times.
15x if (op.cancelled.load(std::memory_order_acquire))
746 ✗ ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
747
748 15x svc_.on_pending(&op);
749
750 15x return std::noop_coroutine();
751 }
752
753 // wait_type::write and wait_type::error: route through the
754 // auxiliary poll reactor. There is no overlapped primitive for
755 // "the send buffer has room" that does not also transfer bytes,
756 // and a write wait must report real writability.
757 23x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
758 23x return std::noop_coroutine();
759 }
760
761 inline void
762 2034x win_tcp_socket::cancel() noexcept
763 {
764
1/2
✓ Branch 2 → 3 taken 2034 times.
✗ Branch 2 → 4 not taken.
2034x if (socket_ != INVALID_SOCKET)
765 {
766 2034x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
767 }
768
769 2034x conn_.request_cancel();
770 2034x rd_.request_cancel();
771 2034x wr_.request_cancel();
772 2034x wt_.request_cancel();
773 // CancelIoEx covers overlapped I/O on the socket but cannot reach
774 // a wait op parked in the auxiliary reactor (no overlapped is
775 // outstanding). Route through the reactor explicitly.
776 2034x svc_.scheduler().cancel_wait(&wt_);
777 2034x }
778
779 inline void
780 27765x win_tcp_socket::close_socket() noexcept
781 {
782 // Flag every op cancelled before tearing down the handle. closesocket()
783 // can complete a pending overlapped op with ERROR_NETNAME_DELETED (not the
784 // MSDN-documented ERROR_OPERATION_ABORTED), which iocp_make_err now maps to
785 // connection_reset -- so a locally-closed op must be short-circuited to
786 // canceled via the flag rather than relying on the raw error, exactly as
787 // cancel() does. The store happens-before the completion is decoded.
788 27765x conn_.request_cancel();
789 27765x rd_.request_cancel();
790 27765x wr_.request_cancel();
791 // wt_ is parked in the aux reactor (no overlapped outstanding), so it also
792 // needs an explicit reactor deregister before the SOCKET handle is closed;
793 // otherwise the reactor would keep polling a dangling fd (and on a Winsock
794 // SOCKET-id reuse the wrong fd could be polled briefly).
795 27765x wt_.request_cancel();
796 27765x svc_.scheduler().cancel_wait(&wt_);
797
798
2/2
✓ Branch 8 → 9 taken 5533 times.
✓ Branch 8 → 12 taken 22232 times.
27765x if (socket_ != INVALID_SOCKET)
799 {
800 5533x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
801 5533x ::closesocket(socket_);
802 5533x socket_ = INVALID_SOCKET;
803 }
804
805 27765x family_ = AF_UNSPEC;
806
807 // Clear cached endpoints
808 27765x local_endpoint_ = endpoint{};
809 27765x remote_endpoint_ = endpoint{};
810 27765x }
811
812 inline std::error_code
813 13x win_tcp_socket::shutdown(tcp_socket::shutdown_type what) noexcept
814 {
815 int how;
816
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 10 times.
✓ Branch 2 → 5 taken 2 times.
✗ Branch 2 → 6 not taken.
13x switch (what)
817 {
818 1x case tcp_socket::shutdown_receive:
819 1x how = SD_RECEIVE;
820 1x break;
821 10x case tcp_socket::shutdown_send:
822 10x how = SD_SEND;
823 10x break;
824 2x case tcp_socket::shutdown_both:
825 2x how = SD_BOTH;
826 2x break;
827 ✗ default:
828 ✗ return make_err(WSAEINVAL);
829 }
830
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 11 taken 12 times.
13x if (::shutdown(socket_, how) != 0)
831 1x return make_err(WSAGetLastError());
832 12x return {};
833 }
834
835 inline native_handle_type
836 25365x win_tcp_socket::native_handle() const noexcept
837 {
838 25365x return static_cast<native_handle_type>(socket_);
839 }
840
841 inline native_handle_type
842 8x win_tcp_socket::release_socket() noexcept
843 {
844 8x SOCKET s = socket_;
845
1/2
✓ Branch 2 → 3 taken 8 times.
✗ Branch 2 → 8 not taken.
8x if (s != INVALID_SOCKET)
846 {
847 8x cancel();
848 // Sever the port association so the descriptor can be
849 // adopted again; best-effort, the caller keeps a working
850 // socket either way.
851 8x dissociate_from_iocp(s);
852 8x socket_ = INVALID_SOCKET;
853 8x family_ = AF_UNSPEC;
854 8x local_endpoint_ = endpoint{};
855 8x remote_endpoint_ = endpoint{};
856 }
857 8x return static_cast<native_handle_type>(s);
858 }
859
860 inline std::error_code
861 3509x win_tcp_socket::set_option(
862 int level, int optname, void const* data, std::size_t size) noexcept
863 {
864 3509x if (::setsockopt(
865 socket_, level, optname, reinterpret_cast<char const*>(data),
866
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 3508 times.
3509x static_cast<int>(size)) != 0)
867 1x return make_err(WSAGetLastError());
868 3508x return {};
869 }
870
871 inline std::error_code
872 42x win_tcp_socket::get_option(
873 int level, int optname, void* data, std::size_t* size) const noexcept
874 {
875 42x int len = static_cast<int>(*size);
876 84x if (::getsockopt(
877
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 41 times.
42x socket_, level, optname, reinterpret_cast<char*>(data), &len) != 0)
878 1x return make_err(WSAGetLastError());
879 41x *size = static_cast<std::size_t>(len);
880 41x return {};
881 }
882
883 inline endpoint
884 29x win_tcp_socket::local_endpoint() const noexcept
885 {
886 29x return local_endpoint_;
887 }
888
889 inline endpoint
890 37x win_tcp_socket::remote_endpoint() const noexcept
891 {
892 37x return remote_endpoint_;
893 }
894
895 // win_tcp_service
896
897 1017x inline win_tcp_service::win_tcp_service(capy::execution_context& ctx)
898 2034x : sched_(ctx.use_service<win_scheduler>())
899
1/1
✓ Branch 5 → 6 taken 1017 times.
1017x , iocp_(sched_.native_handle())
900 {
901
1/1
✓ Branch 9 → 10 taken 1017 times.
1017x load_extension_functions();
902 1017x }
903
904 inline void
905 1016x win_tcp_service::shutdown()
906 {
907 // Close all sockets/acceptors to force pending I/O to complete via
908 // IOCP. Not deleted here: in-flight ops hold their own object_ref, so
909 // each impl stays alive until its cancel completion drains; the
910 // pools' own destructors free whatever is still live once this
911 // service itself is torn down (after the scheduler, which drains
912 // every queued completion first).
913
1/1
✓ Branch 2 → 3 taken 1016 times.
2251x pool_.shutdown([](win_tcp_socket* impl) { impl->close_socket(); });
914
915
1/1
✓ Branch 3 → 4 taken 1016 times.
1016x acceptor_pool_.shutdown(
916 414x [](win_tcp_acceptor* impl) { impl->close_socket(); });
917 1016x }
918
919 inline io_object::implementation*
920 9583x win_tcp_service::construct()
921 {
922 9583x return pool_.acquire(*this);
923 }
924
925 inline void
926 9583x win_tcp_service::destroy(io_object::implementation* p)
927 {
928
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 9583 times.
9583x if (!p)
929 ✗ return;
930 9583x auto* s = static_cast<win_tcp_socket*>(p);
931 9583x s->close_socket();
932 9583x release(s);
933 }
934
935 inline void
936 13897x win_tcp_service::close(io_object::handle& h)
937 {
938 13897x static_cast<win_tcp_socket*>(h.get())->close_socket();
939 13897x }
940
941 inline std::error_code
942 3050x win_tcp_service::open_socket(
943 win_tcp_socket& impl, int family, int type, int protocol)
944 {
945 3050x impl.close_socket();
946
947 SOCKET sock =
948 3050x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
949
950
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 7 taken 3049 times.
3050x if (sock == INVALID_SOCKET)
951 1x return make_err(::WSAGetLastError());
952
953
2/2
✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 10 taken 3039 times.
3049x if (family == AF_INET6)
954 {
955 10x DWORD one = 1;
956
1/1
✓ Branch 8 → 9 taken 10 times.
10x ::setsockopt(
957 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
958 sizeof(one));
959 }
960
961 6098x HANDLE result = ::CreateIoCompletionPort(
962 3049x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
963
964
2/2
✓ Branch 11 → 12 taken 9 times.
✓ Branch 11 → 15 taken 3040 times.
3049x if (result == nullptr)
965 {
966 9x DWORD dwError = ::GetLastError();
967 9x ::closesocket(sock);
968 9x return make_err(dwError);
969 }
970
971 3040x impl.socket_ = sock;
972 3040x impl.family_ = family;
973 3040x return {};
974 }
975
976 inline std::error_code
977 27x win_tcp_service::assign_socket(win_tcp_socket& impl, native_handle_type fd)
978 {
979 27x SOCKET sock = static_cast<SOCKET>(fd);
980
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 4 taken 24 times.
27x if (sock == INVALID_SOCKET)
981 3x return make_err(WSAENOTSOCK);
982
983 // SO_PROTOCOL_INFOW works on an unbound socket, unlike getsockname
984 // (WSAEINVAL until bind/connect names it) -- an adopted socket may
985 // have reached connected state without an explicit bind.
986 24x WSAPROTOCOL_INFOW proto_info{};
987 24x int proto_len = sizeof(proto_info);
988
1/1
✓ Branch 4 → 5 taken 24 times.
24x if (::getsockopt(
989 sock, SOL_SOCKET, SO_PROTOCOL_INFOW,
990
2/2
✓ Branch 5 → 6 taken 9 times.
✓ Branch 5 → 8 taken 15 times.
24x reinterpret_cast<char*>(&proto_info), &proto_len) != 0)
991
1/1
✓ Branch 6 → 7 taken 9 times.
9x return make_err(::WSAGetLastError());
992
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 11 taken 14 times.
15x if (proto_info.iAddressFamily != AF_INET &&
993
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1 time.
1x proto_info.iAddressFamily != AF_INET6)
994 ✗ return make_err(WSAEAFNOSUPPORT);
995
2/2
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 13 times.
15x if (proto_info.iSocketType != SOCK_STREAM)
996 2x return make_err(WSAEPROTOTYPE);
997
998
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 12 times.
13x if (has_skip_on_success(reinterpret_cast<HANDLE>(sock)))
999 1x return std::make_error_code(std::errc::operation_not_supported);
1000
1001 24x HANDLE result = ::CreateIoCompletionPort(
1002
1/1
✓ Branch 16 → 17 taken 12 times.
12x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1003
2/2
✓ Branch 17 → 18 taken 9 times.
✓ Branch 17 → 20 taken 3 times.
12x if (result == nullptr)
1004
1/1
✓ Branch 18 → 19 taken 9 times.
9x return make_err(::GetLastError());
1005
1006 3x impl.socket_ = sock;
1007 3x impl.family_ = proto_info.iAddressFamily;
1008
1009 3x endpoint local_ep, remote_ep;
1010 3x sockaddr_storage local_storage{};
1011 3x int local_len = sizeof(local_storage);
1012
1/1
✓ Branch 20 → 21 taken 3 times.
3x if (::getsockname(
1013
1/2
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 24 not taken.
3x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1014 3x local_ep = detail::from_sockaddr(local_storage);
1015 3x sockaddr_storage remote_storage{};
1016 3x int remote_len = sizeof(remote_storage);
1017
1/1
✓ Branch 24 → 25 taken 3 times.
3x if (::getpeername(
1018
1/2
✓ Branch 25 → 26 taken 3 times.
✗ Branch 25 → 28 not taken.
3x sock, reinterpret_cast<sockaddr*>(&remote_storage), &remote_len) ==
1019 0)
1020 3x remote_ep = detail::from_sockaddr(remote_storage);
1021 3x impl.set_endpoints(local_ep, remote_ep);
1022
1023 3x return {};
1024 }
1025
1026 inline std::error_code
1027 8x win_tcp_service::bind_socket(win_tcp_socket& impl, endpoint ep)
1028 {
1029 8x SOCKET sock = impl.socket_;
1030
1031 8x sockaddr_storage storage{};
1032 8x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1033
1/1
✓ Branch 3 → 4 taken 8 times.
8x if (::bind(
1034 sock, reinterpret_cast<sockaddr*>(&storage),
1035
2/2
✓ Branch 4 → 5 taken 3 times.
✓ Branch 4 → 7 taken 5 times.
8x static_cast<int>(addrlen)) == SOCKET_ERROR)
1036
1/1
✓ Branch 5 → 6 taken 3 times.
3x return make_err(::WSAGetLastError());
1037
1038 // Cache local endpoint (resolves ephemeral port)
1039 5x sockaddr_storage local_storage{};
1040 5x int local_len = sizeof(local_storage);
1041
1/1
✓ Branch 7 → 8 taken 5 times.
5x if (::getsockname(
1042
1/2
✓ Branch 8 → 9 taken 5 times.
✗ Branch 8 → 11 not taken.
5x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1043 5x impl.local_endpoint_ = detail::from_sockaddr(local_storage);
1044
1045 5x return {};
1046 }
1047
1048 inline void*
1049 3303x win_tcp_service::native_handle() const noexcept
1050 {
1051 3303x return iocp_;
1052 }
1053
1054 inline void
1055 win_tcp_service::post(overlapped_op* op)
1056 {
1057 sched_.post(op);
1058 }
1059
1060 inline void
1061 428036x win_tcp_service::on_pending(overlapped_op* op) noexcept
1062 {
1063 428036x sched_.on_pending(op);
1064 428036x }
1065
1066 inline void
1067 535x win_tcp_service::on_completion(
1068 overlapped_op* op, DWORD error, DWORD bytes) noexcept
1069 {
1070 535x sched_.on_completion(op, error, bytes);
1071 535x }
1072
1073 inline void
1074 428604x win_tcp_service::work_started() noexcept
1075 {
1076 428604x sched_.work_started();
1077 428604x }
1078
1079 inline void
1080 win_tcp_service::work_finished() noexcept
1081 {
1082 sched_.work_finished();
1083 }
1084
1085 inline void
1086 1017x win_tcp_service::load_extension_functions()
1087 {
1088
1/1
✓ Branch 2 → 3 taken 1017 times.
1017x SOCKET sock = ::WSASocketW(
1089 AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
1090
1091
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 1016 times.
1017x if (sock == INVALID_SOCKET)
1092 1x return;
1093
1094 1016x DWORD bytes = 0;
1095
1096 1016x GUID connect_ex_guid = WSAID_CONNECTEX;
1097 1016x ::WSAIoctl(
1098 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &connect_ex_guid,
1099
1/1
✓ Branch 5 → 6 taken 1016 times.
1016x sizeof(connect_ex_guid), &connect_ex_, sizeof(connect_ex_), &bytes,
1100 nullptr, nullptr);
1101
1102 1016x GUID accept_ex_guid = WSAID_ACCEPTEX;
1103 1016x ::WSAIoctl(
1104 sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &accept_ex_guid,
1105
1/1
✓ Branch 6 → 7 taken 1016 times.
1016x sizeof(accept_ex_guid), &accept_ex_, sizeof(accept_ex_), &bytes,
1106 nullptr, nullptr);
1107
1108
1/1
✓ Branch 7 → 8 taken 1016 times.
1016x ::closesocket(sock);
1109 }
1110
1111 inline win_tcp_acceptor*
1112 2742x win_tcp_service::acquire_acceptor_impl()
1113 {
1114 2742x return acceptor_pool_.acquire(*this);
1115 }
1116
1117 inline std::error_code
1118 2705x win_tcp_service::open_acceptor_socket(
1119 win_tcp_acceptor& impl, int family, int type, int protocol)
1120 {
1121 2705x impl.close_socket();
1122
1123 SOCKET sock =
1124 2705x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
1125
1126
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 2703 times.
2705x if (sock == INVALID_SOCKET)
1127 2x return make_err(::WSAGetLastError());
1128
1129
2/2
✓ Branch 7 → 8 taken 11 times.
✓ Branch 7 → 10 taken 2692 times.
2703x if (family == AF_INET6)
1130 {
1131 11x DWORD val = 0; // dual-stack default
1132
1/1
✓ Branch 8 → 9 taken 11 times.
11x ::setsockopt(
1133 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&val),
1134 sizeof(val));
1135 }
1136
1137 5406x HANDLE result = ::CreateIoCompletionPort(
1138 2703x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1139
1140
2/2
✓ Branch 11 → 12 taken 9 times.
✓ Branch 11 → 15 taken 2694 times.
2703x if (result == nullptr)
1141 {
1142 9x DWORD dwError = ::GetLastError();
1143 9x ::closesocket(sock);
1144 9x return make_err(dwError);
1145 }
1146
1147 2694x impl.socket_ = sock;
1148 2694x impl.family_ = family;
1149 2694x return {};
1150 }
1151
1152 inline std::error_code
1153 25x win_tcp_service::assign_acceptor_socket(
1154 win_tcp_acceptor& impl, native_handle_type fd)
1155 {
1156 25x SOCKET sock = static_cast<SOCKET>(fd);
1157
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 24 times.
25x if (sock == INVALID_SOCKET)
1158 1x return make_err(WSAENOTSOCK);
1159
1160 // SO_PROTOCOL_INFOW works on an unbound socket, unlike getsockname
1161 // (WSAEINVAL until bind names it).
1162 24x WSAPROTOCOL_INFOW proto_info{};
1163 24x int proto_len = sizeof(proto_info);
1164
1/1
✓ Branch 4 → 5 taken 24 times.
24x if (::getsockopt(
1165 sock, SOL_SOCKET, SO_PROTOCOL_INFOW,
1166
2/2
✓ Branch 5 → 6 taken 9 times.
✓ Branch 5 → 8 taken 15 times.
24x reinterpret_cast<char*>(&proto_info), &proto_len) != 0)
1167
1/1
✓ Branch 6 → 7 taken 9 times.
9x return make_err(::WSAGetLastError());
1168
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 11 taken 14 times.
15x if (proto_info.iAddressFamily != AF_INET &&
1169
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1 time.
1x proto_info.iAddressFamily != AF_INET6)
1170 ✗ return make_err(WSAEAFNOSUPPORT);
1171
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 14 times.
15x if (proto_info.iSocketType != SOCK_STREAM)
1172 1x return make_err(WSAEPROTOTYPE);
1173
1174
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 13 times.
14x if (has_skip_on_success(reinterpret_cast<HANDLE>(sock)))
1175 1x return std::make_error_code(std::errc::operation_not_supported);
1176
1177 26x HANDLE result = ::CreateIoCompletionPort(
1178
1/1
✓ Branch 16 → 17 taken 13 times.
13x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1179
2/2
✓ Branch 17 → 18 taken 9 times.
✓ Branch 17 → 20 taken 4 times.
13x if (result == nullptr)
1180
1/1
✓ Branch 18 → 19 taken 9 times.
9x return make_err(::GetLastError());
1181
1182 4x impl.socket_ = sock;
1183 4x impl.family_ = proto_info.iAddressFamily;
1184
1185 // AcceptEx sizes its address buffers from this cache, so an
1186 // unseeded endpoint breaks accepts on an adopted v6 listener.
1187 4x sockaddr_storage local_storage{};
1188 4x int local_len = sizeof(local_storage);
1189
1/1
✓ Branch 20 → 21 taken 4 times.
4x if (::getsockname(
1190
1/2
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 24 not taken.
4x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1191 4x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1192
1193 4x return {};
1194 }
1195
1196 inline std::error_code
1197 2687x win_tcp_service::bind_acceptor(win_tcp_acceptor& impl, endpoint ep)
1198 {
1199 2687x SOCKET sock = impl.socket_;
1200
1201 2687x sockaddr_storage storage{};
1202 2687x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1203
1/1
✓ Branch 3 → 4 taken 2687 times.
2687x if (::bind(
1204 sock, reinterpret_cast<sockaddr*>(&storage),
1205
2/2
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 7 taken 2679 times.
2687x static_cast<int>(addrlen)) == SOCKET_ERROR)
1206
1/1
✓ Branch 5 → 6 taken 8 times.
8x return make_err(::WSAGetLastError());
1207
1208 // Cache local endpoint (resolves ephemeral port)
1209 2679x sockaddr_storage local_storage{};
1210 2679x int local_len = sizeof(local_storage);
1211
1/1
✓ Branch 7 → 8 taken 2679 times.
2679x if (::getsockname(
1212
1/2
✓ Branch 8 → 9 taken 2679 times.
✗ Branch 8 → 11 not taken.
2679x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1213 2679x impl.set_local_endpoint(detail::from_sockaddr(local_storage));
1214
1215 2679x return {};
1216 }
1217
1218 inline std::error_code
1219 2665x win_tcp_service::listen_acceptor(win_tcp_acceptor& impl, int backlog)
1220 {
1221 2665x SOCKET sock = impl.socket_;
1222
1223
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 2663 times.
2665x if (::listen(sock, backlog) == SOCKET_ERROR)
1224 2x return make_err(::WSAGetLastError());
1225
1226 2663x return {};
1227 }
1228
1229 } // namespace boost::corosio::detail
1230
1231 #endif // BOOST_COROSIO_HAS_IOCP
1232
1233 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TCP_SERVICE_HPP
1234