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

84.4% Lines (357/423) 92.9% List of functions (52/56) 61.9% Branches (99/160)
win_udp_service.hpp
f(x) Functions (56)
Function Calls Lines Branches Blocks
boost::corosio::detail::send_to_op::send_to_op(boost::corosio::detail::win_udp_socket_internal&) :92 52x 100.0% 100.0% boost::corosio::detail::recv_from_op::recv_from_op(boost::corosio::detail::win_udp_socket_internal&) :99 52x 100.0% 100.0% boost::corosio::detail::udp_connect_op::udp_connect_op(boost::corosio::detail::win_udp_socket_internal&) :106 52x 100.0% 100.0% boost::corosio::detail::udp_send_op::udp_send_op(boost::corosio::detail::win_udp_socket_internal&) :114 52x 100.0% 100.0% boost::corosio::detail::udp_recv_op::udp_recv_op(boost::corosio::detail::win_udp_socket_internal&) :121 52x 100.0% 100.0% boost::corosio::detail::send_to_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :131 0 0.0% 0.0% 0.0% boost::corosio::detail::recv_from_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :143 1x 100.0% 50.0% 88.9% boost::corosio::detail::send_to_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :157 13x 66.7% 66.7% 61.5% boost::corosio::detail::recv_from_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :179 20x 75.0% 72.7% 77.3% boost::corosio::detail::udp_connect_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :209 0 0.0% 0.0% boost::corosio::detail::udp_send_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :216 0 0.0% 0.0% 0.0% boost::corosio::detail::udp_recv_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :228 0 0.0% 0.0% 0.0% boost::corosio::detail::udp_connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :242 6x 83.3% 58.3% 72.0% boost::corosio::detail::udp_send_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :276 3x 66.7% 66.7% 61.5% boost::corosio::detail::udp_recv_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :296 2x 66.7% 66.7% 61.5% boost::corosio::detail::win_udp_socket_internal::win_udp_socket_internal(boost::corosio::detail::win_udp_service&) :317 52x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::~win_udp_socket_internal() :328 52x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::native_handle() const :334 272x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::local_endpoint() const :340 18x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::remote_endpoint() const :346 2x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::is_open() const :352 1x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::send_to(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, boost::corosio::endpoint, int, std::stop_token, std::error_code*, unsigned long long*) :358 13x 96.6% 75.0% 94.1% boost::corosio::detail::win_udp_socket_internal::recv_from(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, boost::corosio::endpoint*, int, std::stop_token, std::error_code*, unsigned long long*) :421 20x 91.7% 75.0% 86.4% boost::corosio::detail::win_udp_socket_internal::connect(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::endpoint, std::stop_token, std::error_code*) :494 6x 94.1% 60.0% 85.0% boost::corosio::detail::win_udp_socket_internal::send(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, int, std::stop_token, std::error_code*, unsigned long long*) :528 3x 81.5% 50.0% 75.8% boost::corosio::detail::win_udp_socket_internal::recv(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, int, std::stop_token, std::error_code*, unsigned long long*) :582 2x 81.2% 65.0% 75.0% boost::corosio::detail::win_udp_socket_internal::cancel() :644 3x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_socket_internal::close_socket() :659 200x 100.0% 100.0% 100.0% boost::corosio::detail::win_udp_socket::win_udp_socket(std::shared_ptr<boost::corosio::detail::win_udp_socket_internal>) :676 52x 100.0% 100.0% boost::corosio::detail::win_udp_socket::close_internal() :683 52x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_socket::send_to(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, boost::corosio::endpoint, int, std::stop_token, std::error_code*, unsigned long long*) :693 13x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::recv_from(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, boost::corosio::endpoint*, int, std::stop_token, std::error_code*, unsigned long long*) :707 20x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::connect(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::endpoint, std::stop_token, std::error_code*) :721 6x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::send(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, int, std::stop_token, std::error_code*, unsigned long long*) :732 3x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::recv(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, int, std::stop_token, std::error_code*, unsigned long long*) :745 2x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::native_handle() const :758 253x 100.0% 100.0% boost::corosio::detail::win_udp_socket::set_option(int, int, void const*, unsigned long long) :764 10x 83.3% 50.0% 75.0% boost::corosio::detail::win_udp_socket::get_option(int, int, void*, unsigned long long*) const :775 8x 87.5% 50.0% 77.8% boost::corosio::detail::win_udp_socket::local_endpoint() const :788 18x 100.0% 100.0% boost::corosio::detail::win_udp_socket::remote_endpoint() const :794 2x 100.0% 100.0% boost::corosio::detail::win_udp_socket::cancel() :800 3x 100.0% 100.0% boost::corosio::detail::win_udp_socket::get_internal() const :806 176x 100.0% 100.0% boost::corosio::detail::win_udp_service::win_udp_service(boost::capy::execution_context&) :813 442x 100.0% 100.0% 75.0% boost::corosio::detail::win_udp_service::~win_udp_service() :819 884x 100.0% 100.0% boost::corosio::detail::win_udp_service::shutdown() :827 442x 66.7% 50.0% 71.4% boost::corosio::detail::win_udp_service::construct() :839 52x 100.0% 100.0% 89.5% boost::corosio::detail::win_udp_service::destroy(boost::corosio::io_object::implementation*) :859 52x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::close(boost::corosio::io_object::handle&) :870 100x 100.0% 100.0% boost::corosio::detail::win_udp_service::destroy_impl(boost::corosio::detail::win_udp_socket&) :877 52x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::unregister_impl(boost::corosio::detail::win_udp_socket_internal&) :887 52x 100.0% 100.0% boost::corosio::detail::win_udp_service::open_socket(boost::corosio::detail::win_udp_socket_internal&, int, int, int) :894 48x 76.5% 71.4% 68.8% boost::corosio::detail::win_udp_service::open_datagram_socket(boost::corosio::udp_socket::implementation&, int, int, int) :929 48x 100.0% 100.0% boost::corosio::detail::win_udp_service::bind_datagram(boost::corosio::udp_socket::implementation&, boost::corosio::endpoint) :937 28x 100.0% 85.7% 100.0% boost::corosio::detail::win_udp_service::on_pending(boost::corosio::detail::overlapped_op*) :967 36x 100.0% 100.0% boost::corosio::detail::win_udp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :973 8x 100.0% 100.0% boost::corosio::detail::win_udp_service::work_started() :980 44x 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_UDP_SERVICE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_UDP_SERVICE_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_HAS_IOCP
16
17 #include <boost/corosio/detail/config.hpp>
18 #include <boost/corosio/detail/udp_service.hpp>
19
20 #include <boost/corosio/native/detail/iocp/win_udp_socket.hpp>
21 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
22 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
23 #include <boost/corosio/native/detail/iocp/win_mutex.hpp>
24 #include <boost/corosio/native/detail/iocp/win_wsa_init.hpp>
25
26 #include <boost/corosio/native/detail/endpoint_convert.hpp>
27 #include <boost/corosio/native/detail/make_err.hpp>
28 #include <boost/corosio/detail/dispatch_coro.hpp>
29
30 #include <cstring>
31
32 #include <Ws2tcpip.h>
33
34 namespace boost::corosio::detail {
35
36 /** IOCP UDP service implementation.
37
38 Inherits from udp_service to enable runtime polymorphism.
39 Uses key_type = udp_service for service lookup.
40 */
41 class BOOST_COROSIO_DECL win_udp_service final
42 : private win_wsa_init
43 , public udp_service
44 {
45 public:
46 io_object::implementation* construct() override;
47
48 void destroy(io_object::implementation* p) override;
49
50 void close(io_object::handle& h) override;
51
52 explicit win_udp_service(capy::execution_context& ctx);
53
54 ~win_udp_service();
55
56 win_udp_service(win_udp_service const&) = delete;
57 win_udp_service& operator=(win_udp_service const&) = delete;
58
59 void shutdown() override;
60
61 std::error_code open_datagram_socket(
62 udp_socket::implementation& impl,
63 int family,
64 int type,
65 int protocol) override;
66 std::error_code
67 bind_datagram(udp_socket::implementation& impl, endpoint ep) override;
68
69 void destroy_impl(win_udp_socket& impl);
70
71 void unregister_impl(win_udp_socket_internal& impl);
72
73 std::error_code open_socket(
74 win_udp_socket_internal& impl, int family, int type, int protocol);
75
76 void post(overlapped_op* op);
77 void on_pending(overlapped_op* op) noexcept;
78 void on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept;
79 void work_started() noexcept;
80 void work_finished() noexcept;
81
82 private:
83 win_scheduler& sched_;
84 win_mutex mutex_;
85 intrusive_list<win_udp_socket_internal> socket_list_;
86 intrusive_list<win_udp_socket> wrapper_list_;
87 void* iocp_;
88 };
89
90 // Operation constructors
91
92 52x inline send_to_op::send_to_op(win_udp_socket_internal& internal_) noexcept
93 : overlapped_op(&do_complete)
94 52x , internal(internal_)
95 {
96 52x cancel_func_ = &do_cancel_impl;
97 52x }
98
99 52x inline recv_from_op::recv_from_op(win_udp_socket_internal& internal_) noexcept
100 : overlapped_op(&do_complete)
101 52x , internal(internal_)
102 {
103 52x cancel_func_ = &do_cancel_impl;
104 52x }
105
106 52x inline udp_connect_op::udp_connect_op(
107 52x win_udp_socket_internal& internal_) noexcept
108 : overlapped_op(&do_complete)
109 52x , internal(internal_)
110 {
111 52x cancel_func_ = &do_cancel_impl;
112 52x }
113
114 52x inline udp_send_op::udp_send_op(win_udp_socket_internal& internal_) noexcept
115 : overlapped_op(&do_complete)
116 52x , internal(internal_)
117 {
118 52x cancel_func_ = &do_cancel_impl;
119 52x }
120
121 52x inline udp_recv_op::udp_recv_op(win_udp_socket_internal& internal_) noexcept
122 : overlapped_op(&do_complete)
123 52x , internal(internal_)
124 {
125 52x cancel_func_ = &do_cancel_impl;
126 52x }
127
128 // Cancellation functions
129
130 inline void
131 send_to_op::do_cancel_impl(overlapped_op* base) noexcept
132 {
133 auto* op = static_cast<send_to_op*>(base);
134 op->cancelled.store(true, std::memory_order_release);
135 if (op->internal.is_open())
136 {
137 ::CancelIoEx(
138 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
139 }
140 }
141
142 inline void
143 1x recv_from_op::do_cancel_impl(overlapped_op* base) noexcept
144 {
145 1x auto* op = static_cast<recv_from_op*>(base);
146 1x op->cancelled.store(true, std::memory_order_release);
147
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
148 {
149
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
150 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
151 }
152 1x }
153
154 // send_to_op completion handler
155
156 inline void
157 13x send_to_op::do_complete(
158 void* owner,
159 scheduler_op* base,
160 std::uint32_t /*bytes*/,
161 std::uint32_t /*error*/)
162 {
163 13x auto* op = static_cast<send_to_op*>(base);
164
165
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 13 times.
13x if (!owner)
166 {
167 op->cleanup_only();
168 op->internal_ptr.reset();
169 return;
170 }
171
172 13x auto prevent_premature_destruction = std::move(op->internal_ptr);
173
1/1
✓ Branch 8 → 9 taken 13 times.
13x op->invoke_handler();
174 13x }
175
176 // recv_from_op completion handler
177
178 inline void
179 20x recv_from_op::do_complete(
180 void* owner,
181 scheduler_op* base,
182 std::uint32_t /*bytes*/,
183 std::uint32_t /*error*/)
184 {
185 20x auto* op = static_cast<recv_from_op*>(base);
186
187
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 20 times.
20x if (!owner)
188 {
189 op->cleanup_only();
190 op->internal_ptr.reset();
191 return;
192 }
193
194 // Extract source endpoint on success
195 bool success =
196
3/4
✓ Branch 6 → 7 taken 15 times.
✓ Branch 6 → 10 taken 5 times.
✓ Branch 8 → 9 taken 15 times.
✗ Branch 8 → 10 not taken.
20x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
197
3/4
✓ Branch 11 → 12 taken 15 times.
✓ Branch 11 → 15 taken 5 times.
✓ Branch 12 → 13 taken 15 times.
✗ Branch 12 → 15 not taken.
20x if (success && op->source_out)
198 {
199 15x *op->source_out = from_sockaddr(op->source_storage);
200 }
201
202 20x auto prevent_premature_destruction = std::move(op->internal_ptr);
203
1/1
✓ Branch 17 → 18 taken 20 times.
20x op->invoke_handler();
204 20x }
205
206 // Connected-mode cancellation
207
208 inline void
209 udp_connect_op::do_cancel_impl(overlapped_op* base) noexcept
210 {
211 auto* op = static_cast<udp_connect_op*>(base);
212 op->cancelled.store(true, std::memory_order_release);
213 }
214
215 inline void
216 udp_send_op::do_cancel_impl(overlapped_op* base) noexcept
217 {
218 auto* op = static_cast<udp_send_op*>(base);
219 op->cancelled.store(true, std::memory_order_release);
220 if (op->internal.is_open())
221 {
222 ::CancelIoEx(
223 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
224 }
225 }
226
227 inline void
228 udp_recv_op::do_cancel_impl(overlapped_op* base) noexcept
229 {
230 auto* op = static_cast<udp_recv_op*>(base);
231 op->cancelled.store(true, std::memory_order_release);
232 if (op->internal.is_open())
233 {
234 ::CancelIoEx(
235 reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
236 }
237 }
238
239 // Connected-mode completion handlers
240
241 inline void
242 6x udp_connect_op::do_complete(
243 void* owner,
244 scheduler_op* base,
245 std::uint32_t /*bytes*/,
246 std::uint32_t /*error*/)
247 {
248 6x auto* op = static_cast<udp_connect_op*>(base);
249
250
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 6 times.
6x if (!owner)
251 {
252 op->cleanup_only();
253 op->internal_ptr.reset();
254 return;
255 }
256
257 // Cache endpoints on success
258 bool success =
259
2/4
✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 10 not taken.
✓ Branch 8 → 9 taken 6 times.
✗ Branch 8 → 10 not taken.
6x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
260
1/2
✓ Branch 11 → 12 taken 6 times.
✗ Branch 11 → 17 not taken.
6x if (success)
261 {
262 6x sockaddr_storage local_storage{};
263 6x int local_len = sizeof(local_storage);
264 12x if (::getsockname(
265
1/1
✓ Branch 12 → 13 taken 6 times.
6x op->internal.socket_,
266
1/2
✓ Branch 13 → 14 taken 6 times.
✗ Branch 13 → 16 not taken.
6x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
267 6x op->internal.local_endpoint_ = from_sockaddr(local_storage);
268 6x op->internal.remote_endpoint_ = op->target_endpoint;
269 }
270
271 6x auto prevent_premature_destruction = std::move(op->internal_ptr);
272
1/1
✓ Branch 19 → 20 taken 6 times.
6x op->invoke_handler();
273 6x }
274
275 inline void
276 3x udp_send_op::do_complete(
277 void* owner,
278 scheduler_op* base,
279 std::uint32_t /*bytes*/,
280 std::uint32_t /*error*/)
281 {
282 3x auto* op = static_cast<udp_send_op*>(base);
283
284
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 3 times.
3x if (!owner)
285 {
286 op->cleanup_only();
287 op->internal_ptr.reset();
288 return;
289 }
290
291 3x auto prevent_premature_destruction = std::move(op->internal_ptr);
292
1/1
✓ Branch 8 → 9 taken 3 times.
3x op->invoke_handler();
293 3x }
294
295 inline void
296 2x udp_recv_op::do_complete(
297 void* owner,
298 scheduler_op* base,
299 std::uint32_t /*bytes*/,
300 std::uint32_t /*error*/)
301 {
302 2x auto* op = static_cast<udp_recv_op*>(base);
303
304
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 2 times.
2x if (!owner)
305 {
306 op->cleanup_only();
307 op->internal_ptr.reset();
308 return;
309 }
310
311 2x auto prevent_premature_destruction = std::move(op->internal_ptr);
312
1/1
✓ Branch 8 → 9 taken 2 times.
2x op->invoke_handler();
313 2x }
314
315 // win_udp_socket_internal
316
317 52x inline win_udp_socket_internal::win_udp_socket_internal(
318 52x win_udp_service& svc) noexcept
319 52x : svc_(svc)
320 52x , wr_(*this)
321 52x , rd_(*this)
322 52x , conn_(*this)
323 52x , send_wr_(*this)
324 104x , recv_rd_(*this)
325 {
326 52x }
327
328 52x inline win_udp_socket_internal::~win_udp_socket_internal()
329 {
330 52x svc_.unregister_impl(*this);
331 52x }
332
333 inline SOCKET
334 272x win_udp_socket_internal::native_handle() const noexcept
335 {
336 272x return socket_;
337 }
338
339 inline endpoint
340 18x win_udp_socket_internal::local_endpoint() const noexcept
341 {
342 18x return local_endpoint_;
343 }
344
345 inline endpoint
346 2x win_udp_socket_internal::remote_endpoint() const noexcept
347 {
348 2x return remote_endpoint_;
349 }
350
351 inline bool
352 1x win_udp_socket_internal::is_open() const noexcept
353 {
354 1x return socket_ != INVALID_SOCKET;
355 }
356
357 inline std::coroutine_handle<>
358 13x win_udp_socket_internal::send_to(
359 std::coroutine_handle<> h,
360 capy::executor_ref d,
361 buffer_param param,
362 endpoint dest,
363 int flags,
364 std::stop_token token,
365 std::error_code* ec,
366 std::size_t* bytes_out)
367 {
368 // Keep internal alive during I/O
369
1/1
✓ Branch 2 → 3 taken 13 times.
13x wr_.internal_ptr = shared_from_this();
370
371 13x auto& op = wr_;
372 13x op.reset();
373 13x op.h = h;
374 13x op.ex = d;
375 13x op.ec_out = ec;
376 13x op.bytes_out = bytes_out;
377 13x op.start(token);
378
379 13x svc_.work_started();
380
381 // Prepare buffers
382 13x capy::mutable_buffer bufs[send_to_op::max_buffers];
383 13x op.wsabuf_count =
384 13x static_cast<DWORD>(param.copy_to(bufs, send_to_op::max_buffers));
385
386
2/2
✓ Branch 15 → 12 taken 12 times.
✓ Branch 15 → 16 taken 13 times.
25x for (DWORD i = 0; i < op.wsabuf_count; ++i)
387 {
388 12x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
389 12x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
390 }
391
392 // Prepare destination address
393 13x op.dest_len = static_cast<int>(to_sockaddr(dest, family_, op.dest_storage));
394
395 26x int result = ::WSASendTo(
396 13x socket_, op.wsabufs, op.wsabuf_count, nullptr,
397 static_cast<DWORD>(flags),
398
1/1
✓ Branch 17 → 18 taken 13 times.
13x reinterpret_cast<sockaddr*>(&op.dest_storage), op.dest_len, &op,
399 nullptr);
400
401
2/2
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 25 taken 12 times.
13x if (result == SOCKET_ERROR)
402 {
403
1/1
✓ Branch 19 → 20 taken 1 time.
1x DWORD err = ::WSAGetLastError();
404
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 25 not taken.
1x if (err != WSA_IO_PENDING)
405 {
406 1x svc_.on_completion(&op, err, 0);
407 1x return std::noop_coroutine();
408 }
409 }
410
411 12x svc_.on_pending(&op);
412
413 // Re-check cancellation after I/O is pending
414
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 12 times.
12x if (op.cancelled.load(std::memory_order_acquire))
415 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
416
417 12x return std::noop_coroutine();
418 }
419
420 inline std::coroutine_handle<>
421 20x win_udp_socket_internal::recv_from(
422 std::coroutine_handle<> h,
423 capy::executor_ref d,
424 buffer_param param,
425 endpoint* source,
426 int flags,
427 std::stop_token token,
428 std::error_code* ec,
429 std::size_t* bytes_out)
430 {
431 // Keep internal alive during I/O
432
1/1
✓ Branch 2 → 3 taken 20 times.
20x rd_.internal_ptr = shared_from_this();
433
434 20x auto& op = rd_;
435 20x op.reset();
436 20x op.h = h;
437 20x op.ex = d;
438 20x op.ec_out = ec;
439 20x op.bytes_out = bytes_out;
440 20x op.source_out = source;
441 20x op.start(token);
442
443 20x svc_.work_started();
444
445 // Prepare buffers
446 20x capy::mutable_buffer bufs[recv_from_op::max_buffers];
447 20x op.wsabuf_count =
448 20x static_cast<DWORD>(param.copy_to(bufs, recv_from_op::max_buffers));
449
450 // Handle empty buffer: complete immediately with 0 bytes
451
6/8
✓ Branch 11 → 12 taken 19 times.
✓ Branch 11 → 15 taken 1 time.
✓ Branch 12 → 13 taken 19 times.
✗ Branch 12 → 16 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 19 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 22 taken 19 times.
20x if (op.wsabuf_count == 0 || (op.wsabuf_count == 1 && bufs[0].size() == 0))
452 {
453 1x op.empty_buffer = true;
454 1x svc_.on_completion(&op, 0, 0);
455 1x return std::noop_coroutine();
456 }
457
458
2/2
✓ Branch 26 → 23 taken 19 times.
✓ Branch 26 → 27 taken 19 times.
38x for (DWORD i = 0; i < op.wsabuf_count; ++i)
459 {
460 19x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
461 19x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
462 }
463
464 19x op.flags = static_cast<DWORD>(flags);
465 19x std::memset(&op.source_storage, 0, sizeof(op.source_storage));
466 19x op.source_len = sizeof(op.source_storage);
467
468 38x int result = ::WSARecvFrom(
469 19x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags,
470
1/1
✓ Branch 27 → 28 taken 19 times.
19x reinterpret_cast<sockaddr*>(&op.source_storage), &op.source_len, &op,
471 nullptr);
472
473
2/2
✓ Branch 28 → 29 taken 6 times.
✓ Branch 28 → 35 taken 13 times.
19x if (result == SOCKET_ERROR)
474 {
475
1/1
✓ Branch 29 → 30 taken 6 times.
6x DWORD err = ::WSAGetLastError();
476
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 6 times.
6x if (err != WSA_IO_PENDING)
477 {
478 svc_.on_completion(&op, err, 0);
479 return std::noop_coroutine();
480 }
481 }
482
483 19x svc_.on_pending(&op);
484
485 // Re-check cancellation after I/O is pending
486
1/2
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 19 times.
19x if (op.cancelled.load(std::memory_order_acquire))
487 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
488
489 19x return std::noop_coroutine();
490 }
491
492 // UDP connect is synchronous on Windows
493 inline std::coroutine_handle<>
494 6x win_udp_socket_internal::connect(
495 std::coroutine_handle<> h,
496 capy::executor_ref d,
497 endpoint ep,
498 std::stop_token token,
499 std::error_code* ec)
500 {
501
1/1
✓ Branch 2 → 3 taken 6 times.
6x conn_.internal_ptr = shared_from_this();
502
503 6x auto& op = conn_;
504 6x op.reset();
505 6x op.h = h;
506 6x op.ex = d;
507 6x op.ec_out = ec;
508 6x op.target_endpoint = ep;
509 6x op.start(token);
510
511 6x svc_.work_started();
512
513 6x sockaddr_storage storage{};
514 6x socklen_t addrlen = detail::to_sockaddr(ep, storage);
515
1/1
✓ Branch 11 → 12 taken 6 times.
6x int result = ::WSAConnect(
516 socket_, reinterpret_cast<sockaddr*>(&storage),
517 static_cast<int>(addrlen), nullptr, nullptr, nullptr, nullptr);
518
519
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 6 times.
6x if (result == SOCKET_ERROR)
520 svc_.on_completion(&op, ::WSAGetLastError(), 0);
521 else
522 6x svc_.on_completion(&op, 0, 0);
523
524 6x return std::noop_coroutine();
525 }
526
527 inline std::coroutine_handle<>
528 3x win_udp_socket_internal::send(
529 std::coroutine_handle<> h,
530 capy::executor_ref d,
531 buffer_param param,
532 int flags,
533 std::stop_token token,
534 std::error_code* ec,
535 std::size_t* bytes_out)
536 {
537
1/1
✓ Branch 2 → 3 taken 3 times.
3x send_wr_.internal_ptr = shared_from_this();
538
539 3x auto& op = send_wr_;
540 3x op.reset();
541 3x op.h = h;
542 3x op.ex = d;
543 3x op.ec_out = ec;
544 3x op.bytes_out = bytes_out;
545 3x op.start(token);
546
547 3x svc_.work_started();
548
549 3x capy::mutable_buffer bufs[udp_send_op::max_buffers];
550 3x op.wsabuf_count =
551 3x static_cast<DWORD>(param.copy_to(bufs, udp_send_op::max_buffers));
552
553
2/2
✓ Branch 15 → 12 taken 3 times.
✓ Branch 15 → 16 taken 3 times.
6x for (DWORD i = 0; i < op.wsabuf_count; ++i)
554 {
555 3x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
556 3x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
557 }
558
559 6x int result = ::WSASend(
560
1/1
✓ Branch 16 → 17 taken 3 times.
3x socket_, op.wsabufs, op.wsabuf_count, nullptr,
561 static_cast<DWORD>(flags), &op, nullptr);
562
563
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 24 taken 3 times.
3x if (result == SOCKET_ERROR)
564 {
565 DWORD err = ::WSAGetLastError();
566 if (err != WSA_IO_PENDING)
567 {
568 svc_.on_completion(&op, err, 0);
569 return std::noop_coroutine();
570 }
571 }
572
573 3x svc_.on_pending(&op);
574
575
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 3 times.
3x if (op.cancelled.load(std::memory_order_acquire))
576 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
577
578 3x return std::noop_coroutine();
579 }
580
581 inline std::coroutine_handle<>
582 2x win_udp_socket_internal::recv(
583 std::coroutine_handle<> h,
584 capy::executor_ref d,
585 buffer_param param,
586 int flags,
587 std::stop_token token,
588 std::error_code* ec,
589 std::size_t* bytes_out)
590 {
591
1/1
✓ Branch 2 → 3 taken 2 times.
2x recv_rd_.internal_ptr = shared_from_this();
592
593 2x auto& op = recv_rd_;
594 2x op.reset();
595 2x op.h = h;
596 2x op.ex = d;
597 2x op.ec_out = ec;
598 2x op.bytes_out = bytes_out;
599 2x op.start(token);
600
601 2x svc_.work_started();
602
603 2x capy::mutable_buffer bufs[udp_recv_op::max_buffers];
604 2x op.wsabuf_count =
605 2x static_cast<DWORD>(param.copy_to(bufs, udp_recv_op::max_buffers));
606
607
4/8
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 15 not taken.
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 16 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 2 times.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 22 taken 2 times.
2x if (op.wsabuf_count == 0 || (op.wsabuf_count == 1 && bufs[0].size() == 0))
608 {
609 op.empty_buffer = true;
610 svc_.on_completion(&op, 0, 0);
611 return std::noop_coroutine();
612 }
613
614
2/2
✓ Branch 26 → 23 taken 2 times.
✓ Branch 26 → 27 taken 2 times.
4x for (DWORD i = 0; i < op.wsabuf_count; ++i)
615 {
616 2x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
617 2x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
618 }
619
620 2x op.flags = static_cast<DWORD>(flags);
621
622 4x int result = ::WSARecv(
623
1/1
✓ Branch 27 → 28 taken 2 times.
2x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
624
625
2/2
✓ Branch 28 → 29 taken 1 time.
✓ Branch 28 → 35 taken 1 time.
2x if (result == SOCKET_ERROR)
626 {
627
1/1
✓ Branch 29 → 30 taken 1 time.
1x DWORD err = ::WSAGetLastError();
628
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 1 time.
1x if (err != WSA_IO_PENDING)
629 {
630 svc_.on_completion(&op, err, 0);
631 return std::noop_coroutine();
632 }
633 }
634
635 2x svc_.on_pending(&op);
636
637
1/2
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 2 times.
2x if (op.cancelled.load(std::memory_order_acquire))
638 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
639
640 2x return std::noop_coroutine();
641 }
642
643 inline void
644 3x win_udp_socket_internal::cancel() noexcept
645 {
646
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 4 not taken.
3x if (socket_ != INVALID_SOCKET)
647 {
648 3x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
649 }
650
651 3x wr_.request_cancel();
652 3x rd_.request_cancel();
653 3x conn_.request_cancel();
654 3x send_wr_.request_cancel();
655 3x recv_rd_.request_cancel();
656 3x }
657
658 inline void
659 200x win_udp_socket_internal::close_socket() noexcept
660 {
661
2/2
✓ Branch 2 → 3 taken 48 times.
✓ Branch 2 → 6 taken 152 times.
200x if (socket_ != INVALID_SOCKET)
662 {
663 48x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
664 48x ::closesocket(socket_);
665 48x socket_ = INVALID_SOCKET;
666 }
667
668 200x family_ = AF_UNSPEC;
669
670 200x local_endpoint_ = endpoint{};
671 200x remote_endpoint_ = endpoint{};
672 200x }
673
674 // win_udp_socket
675
676 52x inline win_udp_socket::win_udp_socket(
677 52x std::shared_ptr<win_udp_socket_internal> internal) noexcept
678 52x : internal_(std::move(internal))
679 {
680 52x }
681
682 inline void
683 52x win_udp_socket::close_internal() noexcept
684 {
685
1/2
✓ Branch 3 → 4 taken 52 times.
✗ Branch 3 → 7 not taken.
52x if (internal_)
686 {
687 52x internal_->close_socket();
688 52x internal_.reset();
689 }
690 52x }
691
692 inline std::coroutine_handle<>
693 13x win_udp_socket::send_to(
694 std::coroutine_handle<> h,
695 capy::executor_ref d,
696 buffer_param buf,
697 endpoint dest,
698 int flags,
699 std::stop_token token,
700 std::error_code* ec,
701 std::size_t* bytes)
702 {
703
1/1
✓ Branch 4 → 5 taken 13 times.
13x return internal_->send_to(h, d, buf, dest, flags, token, ec, bytes);
704 }
705
706 inline std::coroutine_handle<>
707 20x win_udp_socket::recv_from(
708 std::coroutine_handle<> h,
709 capy::executor_ref d,
710 buffer_param buf,
711 endpoint* source,
712 int flags,
713 std::stop_token token,
714 std::error_code* ec,
715 std::size_t* bytes)
716 {
717
1/1
✓ Branch 4 → 5 taken 20 times.
20x return internal_->recv_from(h, d, buf, source, flags, token, ec, bytes);
718 }
719
720 inline std::coroutine_handle<>
721 6x win_udp_socket::connect(
722 std::coroutine_handle<> h,
723 capy::executor_ref d,
724 endpoint ep,
725 std::stop_token token,
726 std::error_code* ec)
727 {
728
1/1
✓ Branch 4 → 5 taken 6 times.
6x return internal_->connect(h, d, ep, token, ec);
729 }
730
731 inline std::coroutine_handle<>
732 3x win_udp_socket::send(
733 std::coroutine_handle<> h,
734 capy::executor_ref d,
735 buffer_param buf,
736 int flags,
737 std::stop_token token,
738 std::error_code* ec,
739 std::size_t* bytes)
740 {
741
1/1
✓ Branch 4 → 5 taken 3 times.
3x return internal_->send(h, d, buf, flags, token, ec, bytes);
742 }
743
744 inline std::coroutine_handle<>
745 2x win_udp_socket::recv(
746 std::coroutine_handle<> h,
747 capy::executor_ref d,
748 buffer_param buf,
749 int flags,
750 std::stop_token token,
751 std::error_code* ec,
752 std::size_t* bytes)
753 {
754
1/1
✓ Branch 4 → 5 taken 2 times.
2x return internal_->recv(h, d, buf, flags, token, ec, bytes);
755 }
756
757 inline native_handle_type
758 253x win_udp_socket::native_handle() const noexcept
759 {
760 253x return static_cast<native_handle_type>(internal_->native_handle());
761 }
762
763 inline std::error_code
764 10x win_udp_socket::set_option(
765 int level, int optname, void const* data, std::size_t size) noexcept
766 {
767 10x if (::setsockopt(
768 10x internal_->native_handle(), level, optname,
769
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 10 times.
10x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
770 return make_err(WSAGetLastError());
771 10x return {};
772 }
773
774 inline std::error_code
775 8x win_udp_socket::get_option(
776 int level, int optname, void* data, std::size_t* size) const noexcept
777 {
778 8x int len = static_cast<int>(*size);
779 8x if (::getsockopt(
780 8x internal_->native_handle(), level, optname,
781
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 8 times.
8x reinterpret_cast<char*>(data), &len) != 0)
782 return make_err(WSAGetLastError());
783 8x *size = static_cast<std::size_t>(len);
784 8x return {};
785 }
786
787 inline endpoint
788 18x win_udp_socket::local_endpoint() const noexcept
789 {
790 18x return internal_->local_endpoint();
791 }
792
793 inline endpoint
794 2x win_udp_socket::remote_endpoint() const noexcept
795 {
796 2x return internal_->remote_endpoint();
797 }
798
799 inline void
800 3x win_udp_socket::cancel() noexcept
801 {
802 3x internal_->cancel();
803 3x }
804
805 inline win_udp_socket_internal*
806 176x win_udp_socket::get_internal() const noexcept
807 {
808 176x return internal_.get();
809 }
810
811 // win_udp_service
812
813 442x inline win_udp_service::win_udp_service(capy::execution_context& ctx)
814 884x : sched_(ctx.use_service<win_scheduler>())
815
2/2
✓ Branch 4 → 5 taken 442 times.
✓ Branch 5 → 6 taken 442 times.
442x , iocp_(sched_.native_handle())
816 {
817 442x }
818
819 884x inline win_udp_service::~win_udp_service()
820 {
821
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 442 times.
442x for (auto* w = wrapper_list_.pop_front(); w != nullptr;
822 w = wrapper_list_.pop_front())
823 delete w;
824 884x }
825
826 inline void
827 442x win_udp_service::shutdown()
828 {
829 442x std::lock_guard<win_mutex> lock(mutex_);
830
831
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 442 times.
442x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
832 impl = socket_list_.pop_front())
833 {
834 impl->close_socket();
835 }
836 442x }
837
838 inline io_object::implementation*
839 52x win_udp_service::construct()
840 {
841
1/1
✓ Branch 2 → 3 taken 52 times.
52x auto internal = std::make_shared<win_udp_socket_internal>(*this);
842
843 {
844 52x std::lock_guard<win_mutex> lock(mutex_);
845 52x socket_list_.push_back(internal.get());
846 52x }
847
848
1/1
✓ Branch 7 → 8 taken 52 times.
52x auto* wrapper = new win_udp_socket(std::move(internal));
849
850 {
851 52x std::lock_guard<win_mutex> lock(mutex_);
852 52x wrapper_list_.push_back(wrapper);
853 52x }
854
855 52x return wrapper;
856 52x }
857
858 inline void
859 52x win_udp_service::destroy(io_object::implementation* p)
860 {
861
1/2
✓ Branch 2 → 3 taken 52 times.
✗ Branch 2 → 5 not taken.
52x if (p)
862 {
863 52x auto& wrapper = static_cast<win_udp_socket&>(*p);
864 52x wrapper.close_internal();
865 52x destroy_impl(wrapper);
866 }
867 52x }
868
869 inline void
870 100x win_udp_service::close(io_object::handle& h)
871 {
872 100x auto& wrapper = static_cast<win_udp_socket&>(*h.get());
873 100x wrapper.get_internal()->close_socket();
874 100x }
875
876 inline void
877 52x win_udp_service::destroy_impl(win_udp_socket& impl)
878 {
879 {
880 52x std::lock_guard<win_mutex> lock(mutex_);
881 52x wrapper_list_.remove(&impl);
882 52x }
883
1/2
✓ Branch 5 → 6 taken 52 times.
✗ Branch 5 → 7 not taken.
52x delete &impl;
884 52x }
885
886 inline void
887 52x win_udp_service::unregister_impl(win_udp_socket_internal& impl)
888 {
889 52x std::lock_guard<win_mutex> lock(mutex_);
890 52x socket_list_.remove(&impl);
891 52x }
892
893 inline std::error_code
894 48x win_udp_service::open_socket(
895 win_udp_socket_internal& impl, int family, int type, int protocol)
896 {
897 48x impl.close_socket();
898
899 SOCKET sock =
900 48x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
901
902
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 48 times.
48x if (sock == INVALID_SOCKET)
903 return make_err(::WSAGetLastError());
904
905
2/2
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 10 taken 41 times.
48x if (family == AF_INET6)
906 {
907 7x DWORD one = 1;
908
1/1
✓ Branch 8 → 9 taken 7 times.
7x ::setsockopt(
909 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
910 sizeof(one));
911 }
912
913 96x HANDLE result = ::CreateIoCompletionPort(
914 48x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
915
916
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 48 times.
48x if (result == nullptr)
917 {
918 DWORD dwError = ::GetLastError();
919 ::closesocket(sock);
920 return make_err(dwError);
921 }
922
923 48x impl.socket_ = sock;
924 48x impl.family_ = family;
925 48x return {};
926 }
927
928 inline std::error_code
929 48x win_udp_service::open_datagram_socket(
930 udp_socket::implementation& impl, int family, int type, int protocol)
931 {
932 48x auto& wrapper = static_cast<win_udp_socket&>(impl);
933 48x return open_socket(*wrapper.get_internal(), family, type, protocol);
934 }
935
936 inline std::error_code
937 28x win_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
938 {
939 28x auto& wrapper = static_cast<win_udp_socket&>(impl);
940 28x auto* internal = wrapper.get_internal();
941 28x SOCKET sock = internal->socket_;
942
943 28x sockaddr_storage storage{};
944 28x socklen_t addrlen = detail::to_sockaddr(ep, storage);
945
1/1
✓ Branch 4 → 5 taken 28 times.
28x if (::bind(
946 sock, reinterpret_cast<sockaddr*>(&storage),
947
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 26 times.
28x static_cast<int>(addrlen)) == SOCKET_ERROR)
948
1/1
✓ Branch 6 → 7 taken 2 times.
2x return make_err(::WSAGetLastError());
949
950 // Cache local endpoint (resolves ephemeral port)
951 26x sockaddr_storage local_storage{};
952 26x int local_len = sizeof(local_storage);
953
1/1
✓ Branch 8 → 9 taken 26 times.
26x if (::getsockname(
954
1/2
✓ Branch 9 → 10 taken 26 times.
✗ Branch 9 → 12 not taken.
26x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
955 26x internal->local_endpoint_ = detail::from_sockaddr(local_storage);
956
957 26x return {};
958 }
959
960 inline void
961 win_udp_service::post(overlapped_op* op)
962 {
963 sched_.post(op);
964 }
965
966 inline void
967 36x win_udp_service::on_pending(overlapped_op* op) noexcept
968 {
969 36x sched_.on_pending(op);
970 36x }
971
972 inline void
973 8x win_udp_service::on_completion(
974 overlapped_op* op, DWORD error, DWORD bytes) noexcept
975 {
976 8x sched_.on_completion(op, error, bytes);
977 8x }
978
979 inline void
980 44x win_udp_service::work_started() noexcept
981 {
982 44x sched_.work_started();
983 44x }
984
985 inline void
986 win_udp_service::work_finished() noexcept
987 {
988 sched_.work_finished();
989 }
990
991 } // namespace boost::corosio::detail
992
993 #endif // BOOST_COROSIO_HAS_IOCP
994
995 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_UDP_SERVICE_HPP
996