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

84.2% Lines (356/423) 92.9% List of functions (52/56) 60.6% Branches (97/160)
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 48x 100.0% 100.0% boost::corosio::detail::recv_from_op::recv_from_op(boost::corosio::detail::win_udp_socket_internal&) :99 48x 100.0% 100.0% boost::corosio::detail::udp_connect_op::udp_connect_op(boost::corosio::detail::win_udp_socket_internal&) :106 48x 100.0% 100.0% boost::corosio::detail::udp_send_op::udp_send_op(boost::corosio::detail::win_udp_socket_internal&) :114 48x 100.0% 100.0% boost::corosio::detail::udp_recv_op::udp_recv_op(boost::corosio::detail::win_udp_socket_internal&) :121 48x 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 48x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::~win_udp_socket_internal() :328 48x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::native_handle() const :334 257x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::local_endpoint() const :340 17x 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, 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*, std::stop_token, std::error_code*, unsigned long long*) :419 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*) :491 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, std::stop_token, std::error_code*, unsigned long long*) :525 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, std::stop_token, std::error_code*, unsigned long long*) :577 2x 81.2% 65.0% 75.0% boost::corosio::detail::win_udp_socket_internal::cancel() :638 3x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_socket_internal::close_socket() :653 186x 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>) :670 48x 100.0% 100.0% boost::corosio::detail::win_udp_socket::close_internal() :677 48x 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, std::stop_token, std::error_code*, unsigned long long*) :687 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*, std::stop_token, std::error_code*, unsigned long long*) :700 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*) :713 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, std::stop_token, std::error_code*, unsigned long long*) :724 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, std::stop_token, std::error_code*, unsigned long long*) :736 2x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::native_handle() const :748 238x 100.0% 100.0% boost::corosio::detail::win_udp_socket::set_option(int, int, void const*, unsigned long long) :754 10x 83.3% 50.0% 75.0% boost::corosio::detail::win_udp_socket::get_option(int, int, void*, unsigned long long*) const :765 8x 87.5% 50.0% 77.8% boost::corosio::detail::win_udp_socket::local_endpoint() const :778 17x 100.0% 100.0% boost::corosio::detail::win_udp_socket::remote_endpoint() const :784 2x 100.0% 100.0% boost::corosio::detail::win_udp_socket::cancel() :790 3x 100.0% 100.0% boost::corosio::detail::win_udp_socket::get_internal() const :796 163x 100.0% 100.0% boost::corosio::detail::win_udp_service::win_udp_service(boost::capy::execution_context&) :803 369x 100.0% 100.0% 75.0% boost::corosio::detail::win_udp_service::~win_udp_service() :809 738x 100.0% 100.0% boost::corosio::detail::win_udp_service::shutdown() :817 369x 66.7% 50.0% 71.4% boost::corosio::detail::win_udp_service::construct() :829 48x 100.0% 100.0% 89.5% boost::corosio::detail::win_udp_service::destroy(boost::corosio::io_object::implementation*) :849 48x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::close(boost::corosio::io_object::handle&) :860 93x 100.0% 100.0% boost::corosio::detail::win_udp_service::destroy_impl(boost::corosio::detail::win_udp_socket&) :867 48x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::unregister_impl(boost::corosio::detail::win_udp_socket_internal&) :877 48x 100.0% 100.0% boost::corosio::detail::win_udp_service::open_socket(boost::corosio::detail::win_udp_socket_internal&, int, int, int) :884 45x 76.5% 71.4% 68.8% boost::corosio::detail::win_udp_service::open_datagram_socket(boost::corosio::udp_socket::implementation&, int, int, int) :919 45x 100.0% 100.0% boost::corosio::detail::win_udp_service::bind_datagram(boost::corosio::udp_socket::implementation&, boost::corosio::endpoint) :927 25x 93.3% 57.1% 85.7% boost::corosio::detail::win_udp_service::on_pending(boost::corosio::detail::overlapped_op*) :957 36x 100.0% 100.0% boost::corosio::detail::win_udp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :963 8x 100.0% 100.0% boost::corosio::detail::win_udp_service::work_started() :970 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 48x inline send_to_op::send_to_op(win_udp_socket_internal& internal_) noexcept
93 : overlapped_op(&do_complete)
94 48x , internal(internal_)
95 {
96 48x cancel_func_ = &do_cancel_impl;
97 48x }
98
99 48x inline recv_from_op::recv_from_op(win_udp_socket_internal& internal_) noexcept
100 : overlapped_op(&do_complete)
101 48x , internal(internal_)
102 {
103 48x cancel_func_ = &do_cancel_impl;
104 48x }
105
106 48x inline udp_connect_op::udp_connect_op(
107 48x win_udp_socket_internal& internal_) noexcept
108 : overlapped_op(&do_complete)
109 48x , internal(internal_)
110 {
111 48x cancel_func_ = &do_cancel_impl;
112 48x }
113
114 48x inline udp_send_op::udp_send_op(win_udp_socket_internal& internal_) noexcept
115 : overlapped_op(&do_complete)
116 48x , internal(internal_)
117 {
118 48x cancel_func_ = &do_cancel_impl;
119 48x }
120
121 48x inline udp_recv_op::udp_recv_op(win_udp_socket_internal& internal_) noexcept
122 : overlapped_op(&do_complete)
123 48x , internal(internal_)
124 {
125 48x cancel_func_ = &do_cancel_impl;
126 48x }
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 48x inline win_udp_socket_internal::win_udp_socket_internal(
318 48x win_udp_service& svc) noexcept
319 48x : svc_(svc)
320 48x , wr_(*this)
321 48x , rd_(*this)
322 48x , conn_(*this)
323 48x , send_wr_(*this)
324 96x , recv_rd_(*this)
325 {
326 48x }
327
328 48x inline win_udp_socket_internal::~win_udp_socket_internal()
329 {
330 48x svc_.unregister_impl(*this);
331 48x }
332
333 inline SOCKET
334 257x win_udp_socket_internal::native_handle() const noexcept
335 {
336 257x return socket_;
337 }
338
339 inline endpoint
340 17x win_udp_socket_internal::local_endpoint() const noexcept
341 {
342 17x 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 std::stop_token token,
364 std::error_code* ec,
365 std::size_t* bytes_out)
366 {
367 // Keep internal alive during I/O
368
1/1
✓ Branch 2 → 3 taken 13 times.
13x wr_.internal_ptr = shared_from_this();
369
370 13x auto& op = wr_;
371 13x op.reset();
372 13x op.h = h;
373 13x op.ex = d;
374 13x op.ec_out = ec;
375 13x op.bytes_out = bytes_out;
376 13x op.start(token);
377
378 13x svc_.work_started();
379
380 // Prepare buffers
381 13x capy::mutable_buffer bufs[send_to_op::max_buffers];
382 13x op.wsabuf_count =
383 13x static_cast<DWORD>(param.copy_to(bufs, send_to_op::max_buffers));
384
385
2/2
✓ Branch 15 → 12 taken 12 times.
✓ Branch 15 → 16 taken 13 times.
25x for (DWORD i = 0; i < op.wsabuf_count; ++i)
386 {
387 12x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
388 12x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
389 }
390
391 // Prepare destination address
392 13x op.dest_len = static_cast<int>(to_sockaddr(dest, family_, op.dest_storage));
393
394 26x int result = ::WSASendTo(
395 13x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0,
396
1/1
✓ Branch 17 → 18 taken 13 times.
13x reinterpret_cast<sockaddr*>(&op.dest_storage), op.dest_len, &op,
397 nullptr);
398
399
2/2
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 25 taken 12 times.
13x if (result == SOCKET_ERROR)
400 {
401
1/1
✓ Branch 19 → 20 taken 1 time.
1x DWORD err = ::WSAGetLastError();
402
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 25 not taken.
1x if (err != WSA_IO_PENDING)
403 {
404 1x svc_.on_completion(&op, err, 0);
405 1x return std::noop_coroutine();
406 }
407 }
408
409 12x svc_.on_pending(&op);
410
411 // Re-check cancellation after I/O is pending
412
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 12 times.
12x if (op.cancelled.load(std::memory_order_acquire))
413 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
414
415 12x return std::noop_coroutine();
416 }
417
418 inline std::coroutine_handle<>
419 20x win_udp_socket_internal::recv_from(
420 std::coroutine_handle<> h,
421 capy::executor_ref d,
422 buffer_param param,
423 endpoint* source,
424 std::stop_token token,
425 std::error_code* ec,
426 std::size_t* bytes_out)
427 {
428 // Keep internal alive during I/O
429
1/1
✓ Branch 2 → 3 taken 20 times.
20x rd_.internal_ptr = shared_from_this();
430
431 20x auto& op = rd_;
432 20x op.reset();
433 20x op.h = h;
434 20x op.ex = d;
435 20x op.ec_out = ec;
436 20x op.bytes_out = bytes_out;
437 20x op.source_out = source;
438 20x op.start(token);
439
440 20x svc_.work_started();
441
442 // Prepare buffers
443 20x capy::mutable_buffer bufs[recv_from_op::max_buffers];
444 20x op.wsabuf_count =
445 20x static_cast<DWORD>(param.copy_to(bufs, recv_from_op::max_buffers));
446
447 // Handle empty buffer: complete immediately with 0 bytes
448
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))
449 {
450 1x op.empty_buffer = true;
451 1x svc_.on_completion(&op, 0, 0);
452 1x return std::noop_coroutine();
453 }
454
455
2/2
✓ Branch 26 → 23 taken 19 times.
✓ Branch 26 → 27 taken 19 times.
38x for (DWORD i = 0; i < op.wsabuf_count; ++i)
456 {
457 19x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
458 19x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
459 }
460
461 19x op.flags = 0;
462 19x std::memset(&op.source_storage, 0, sizeof(op.source_storage));
463 19x op.source_len = sizeof(op.source_storage);
464
465 38x int result = ::WSARecvFrom(
466 19x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags,
467
1/1
✓ Branch 27 → 28 taken 19 times.
19x reinterpret_cast<sockaddr*>(&op.source_storage), &op.source_len, &op,
468 nullptr);
469
470
2/2
✓ Branch 28 → 29 taken 6 times.
✓ Branch 28 → 35 taken 13 times.
19x if (result == SOCKET_ERROR)
471 {
472
1/1
✓ Branch 29 → 30 taken 6 times.
6x DWORD err = ::WSAGetLastError();
473
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 6 times.
6x if (err != WSA_IO_PENDING)
474 {
475 svc_.on_completion(&op, err, 0);
476 return std::noop_coroutine();
477 }
478 }
479
480 19x svc_.on_pending(&op);
481
482 // Re-check cancellation after I/O is pending
483
1/2
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 19 times.
19x if (op.cancelled.load(std::memory_order_acquire))
484 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
485
486 19x return std::noop_coroutine();
487 }
488
489 // UDP connect is synchronous on Windows
490 inline std::coroutine_handle<>
491 6x win_udp_socket_internal::connect(
492 std::coroutine_handle<> h,
493 capy::executor_ref d,
494 endpoint ep,
495 std::stop_token token,
496 std::error_code* ec)
497 {
498
1/1
✓ Branch 2 → 3 taken 6 times.
6x conn_.internal_ptr = shared_from_this();
499
500 6x auto& op = conn_;
501 6x op.reset();
502 6x op.h = h;
503 6x op.ex = d;
504 6x op.ec_out = ec;
505 6x op.target_endpoint = ep;
506 6x op.start(token);
507
508 6x svc_.work_started();
509
510 6x sockaddr_storage storage{};
511 6x socklen_t addrlen = detail::to_sockaddr(ep, storage);
512
1/1
✓ Branch 11 → 12 taken 6 times.
6x int result = ::WSAConnect(
513 socket_, reinterpret_cast<sockaddr*>(&storage),
514 static_cast<int>(addrlen), nullptr, nullptr, nullptr, nullptr);
515
516
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 6 times.
6x if (result == SOCKET_ERROR)
517 svc_.on_completion(&op, ::WSAGetLastError(), 0);
518 else
519 6x svc_.on_completion(&op, 0, 0);
520
521 6x return std::noop_coroutine();
522 }
523
524 inline std::coroutine_handle<>
525 3x win_udp_socket_internal::send(
526 std::coroutine_handle<> h,
527 capy::executor_ref d,
528 buffer_param param,
529 std::stop_token token,
530 std::error_code* ec,
531 std::size_t* bytes_out)
532 {
533
1/1
✓ Branch 2 → 3 taken 3 times.
3x send_wr_.internal_ptr = shared_from_this();
534
535 3x auto& op = send_wr_;
536 3x op.reset();
537 3x op.h = h;
538 3x op.ex = d;
539 3x op.ec_out = ec;
540 3x op.bytes_out = bytes_out;
541 3x op.start(token);
542
543 3x svc_.work_started();
544
545 3x capy::mutable_buffer bufs[udp_send_op::max_buffers];
546 3x op.wsabuf_count =
547 3x static_cast<DWORD>(param.copy_to(bufs, udp_send_op::max_buffers));
548
549
2/2
✓ Branch 15 → 12 taken 3 times.
✓ Branch 15 → 16 taken 3 times.
6x for (DWORD i = 0; i < op.wsabuf_count; ++i)
550 {
551 3x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
552 3x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
553 }
554
555 6x int result = ::WSASend(
556
1/1
✓ Branch 16 → 17 taken 3 times.
3x socket_, op.wsabufs, op.wsabuf_count, nullptr, 0, &op, nullptr);
557
558
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 24 taken 3 times.
3x if (result == SOCKET_ERROR)
559 {
560 DWORD err = ::WSAGetLastError();
561 if (err != WSA_IO_PENDING)
562 {
563 svc_.on_completion(&op, err, 0);
564 return std::noop_coroutine();
565 }
566 }
567
568 3x svc_.on_pending(&op);
569
570
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 3 times.
3x if (op.cancelled.load(std::memory_order_acquire))
571 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
572
573 3x return std::noop_coroutine();
574 }
575
576 inline std::coroutine_handle<>
577 2x win_udp_socket_internal::recv(
578 std::coroutine_handle<> h,
579 capy::executor_ref d,
580 buffer_param param,
581 std::stop_token token,
582 std::error_code* ec,
583 std::size_t* bytes_out)
584 {
585
1/1
✓ Branch 2 → 3 taken 2 times.
2x recv_rd_.internal_ptr = shared_from_this();
586
587 2x auto& op = recv_rd_;
588 2x op.reset();
589 2x op.h = h;
590 2x op.ex = d;
591 2x op.ec_out = ec;
592 2x op.bytes_out = bytes_out;
593 2x op.start(token);
594
595 2x svc_.work_started();
596
597 2x capy::mutable_buffer bufs[udp_recv_op::max_buffers];
598 2x op.wsabuf_count =
599 2x static_cast<DWORD>(param.copy_to(bufs, udp_recv_op::max_buffers));
600
601
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))
602 {
603 op.empty_buffer = true;
604 svc_.on_completion(&op, 0, 0);
605 return std::noop_coroutine();
606 }
607
608
2/2
✓ Branch 26 → 23 taken 2 times.
✓ Branch 26 → 27 taken 2 times.
4x for (DWORD i = 0; i < op.wsabuf_count; ++i)
609 {
610 2x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
611 2x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
612 }
613
614 2x op.flags = 0;
615
616 4x int result = ::WSARecv(
617
1/1
✓ Branch 27 → 28 taken 2 times.
2x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
618
619
2/2
✓ Branch 28 → 29 taken 1 time.
✓ Branch 28 → 35 taken 1 time.
2x if (result == SOCKET_ERROR)
620 {
621
1/1
✓ Branch 29 → 30 taken 1 time.
1x DWORD err = ::WSAGetLastError();
622
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 1 time.
1x if (err != WSA_IO_PENDING)
623 {
624 svc_.on_completion(&op, err, 0);
625 return std::noop_coroutine();
626 }
627 }
628
629 2x svc_.on_pending(&op);
630
631
1/2
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 2 times.
2x if (op.cancelled.load(std::memory_order_acquire))
632 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
633
634 2x return std::noop_coroutine();
635 }
636
637 inline void
638 3x win_udp_socket_internal::cancel() noexcept
639 {
640
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 4 not taken.
3x if (socket_ != INVALID_SOCKET)
641 {
642 3x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
643 }
644
645 3x wr_.request_cancel();
646 3x rd_.request_cancel();
647 3x conn_.request_cancel();
648 3x send_wr_.request_cancel();
649 3x recv_rd_.request_cancel();
650 3x }
651
652 inline void
653 186x win_udp_socket_internal::close_socket() noexcept
654 {
655
2/2
✓ Branch 2 → 3 taken 45 times.
✓ Branch 2 → 6 taken 141 times.
186x if (socket_ != INVALID_SOCKET)
656 {
657 45x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
658 45x ::closesocket(socket_);
659 45x socket_ = INVALID_SOCKET;
660 }
661
662 186x family_ = AF_UNSPEC;
663
664 186x local_endpoint_ = endpoint{};
665 186x remote_endpoint_ = endpoint{};
666 186x }
667
668 // win_udp_socket
669
670 48x inline win_udp_socket::win_udp_socket(
671 48x std::shared_ptr<win_udp_socket_internal> internal) noexcept
672 48x : internal_(std::move(internal))
673 {
674 48x }
675
676 inline void
677 48x win_udp_socket::close_internal() noexcept
678 {
679
1/2
✓ Branch 3 → 4 taken 48 times.
✗ Branch 3 → 7 not taken.
48x if (internal_)
680 {
681 48x internal_->close_socket();
682 48x internal_.reset();
683 }
684 48x }
685
686 inline std::coroutine_handle<>
687 13x win_udp_socket::send_to(
688 std::coroutine_handle<> h,
689 capy::executor_ref d,
690 buffer_param buf,
691 endpoint dest,
692 std::stop_token token,
693 std::error_code* ec,
694 std::size_t* bytes)
695 {
696
1/1
✓ Branch 4 → 5 taken 13 times.
13x return internal_->send_to(h, d, buf, dest, token, ec, bytes);
697 }
698
699 inline std::coroutine_handle<>
700 20x win_udp_socket::recv_from(
701 std::coroutine_handle<> h,
702 capy::executor_ref d,
703 buffer_param buf,
704 endpoint* source,
705 std::stop_token token,
706 std::error_code* ec,
707 std::size_t* bytes)
708 {
709
1/1
✓ Branch 4 → 5 taken 20 times.
20x return internal_->recv_from(h, d, buf, source, token, ec, bytes);
710 }
711
712 inline std::coroutine_handle<>
713 6x win_udp_socket::connect(
714 std::coroutine_handle<> h,
715 capy::executor_ref d,
716 endpoint ep,
717 std::stop_token token,
718 std::error_code* ec)
719 {
720
1/1
✓ Branch 4 → 5 taken 6 times.
6x return internal_->connect(h, d, ep, token, ec);
721 }
722
723 inline std::coroutine_handle<>
724 3x win_udp_socket::send(
725 std::coroutine_handle<> h,
726 capy::executor_ref d,
727 buffer_param buf,
728 std::stop_token token,
729 std::error_code* ec,
730 std::size_t* bytes)
731 {
732
1/1
✓ Branch 4 → 5 taken 3 times.
3x return internal_->send(h, d, buf, token, ec, bytes);
733 }
734
735 inline std::coroutine_handle<>
736 2x win_udp_socket::recv(
737 std::coroutine_handle<> h,
738 capy::executor_ref d,
739 buffer_param buf,
740 std::stop_token token,
741 std::error_code* ec,
742 std::size_t* bytes)
743 {
744
1/1
✓ Branch 4 → 5 taken 2 times.
2x return internal_->recv(h, d, buf, token, ec, bytes);
745 }
746
747 inline native_handle_type
748 238x win_udp_socket::native_handle() const noexcept
749 {
750 238x return static_cast<native_handle_type>(internal_->native_handle());
751 }
752
753 inline std::error_code
754 10x win_udp_socket::set_option(
755 int level, int optname, void const* data, std::size_t size) noexcept
756 {
757 10x if (::setsockopt(
758 10x internal_->native_handle(), level, optname,
759
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 10 times.
10x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
760 return make_err(WSAGetLastError());
761 10x return {};
762 }
763
764 inline std::error_code
765 8x win_udp_socket::get_option(
766 int level, int optname, void* data, std::size_t* size) const noexcept
767 {
768 8x int len = static_cast<int>(*size);
769 8x if (::getsockopt(
770 8x internal_->native_handle(), level, optname,
771
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 8 times.
8x reinterpret_cast<char*>(data), &len) != 0)
772 return make_err(WSAGetLastError());
773 8x *size = static_cast<std::size_t>(len);
774 8x return {};
775 }
776
777 inline endpoint
778 17x win_udp_socket::local_endpoint() const noexcept
779 {
780 17x return internal_->local_endpoint();
781 }
782
783 inline endpoint
784 2x win_udp_socket::remote_endpoint() const noexcept
785 {
786 2x return internal_->remote_endpoint();
787 }
788
789 inline void
790 3x win_udp_socket::cancel() noexcept
791 {
792 3x internal_->cancel();
793 3x }
794
795 inline win_udp_socket_internal*
796 163x win_udp_socket::get_internal() const noexcept
797 {
798 163x return internal_.get();
799 }
800
801 // win_udp_service
802
803 369x inline win_udp_service::win_udp_service(capy::execution_context& ctx)
804 738x : sched_(ctx.use_service<win_scheduler>())
805
2/2
✓ Branch 4 → 5 taken 369 times.
✓ Branch 5 → 6 taken 369 times.
369x , iocp_(sched_.native_handle())
806 {
807 369x }
808
809 738x inline win_udp_service::~win_udp_service()
810 {
811
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 369 times.
369x for (auto* w = wrapper_list_.pop_front(); w != nullptr;
812 w = wrapper_list_.pop_front())
813 delete w;
814 738x }
815
816 inline void
817 369x win_udp_service::shutdown()
818 {
819 369x std::lock_guard<win_mutex> lock(mutex_);
820
821
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 369 times.
369x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
822 impl = socket_list_.pop_front())
823 {
824 impl->close_socket();
825 }
826 369x }
827
828 inline io_object::implementation*
829 48x win_udp_service::construct()
830 {
831
1/1
✓ Branch 2 → 3 taken 48 times.
48x auto internal = std::make_shared<win_udp_socket_internal>(*this);
832
833 {
834 48x std::lock_guard<win_mutex> lock(mutex_);
835 48x socket_list_.push_back(internal.get());
836 48x }
837
838
1/1
✓ Branch 7 → 8 taken 48 times.
48x auto* wrapper = new win_udp_socket(std::move(internal));
839
840 {
841 48x std::lock_guard<win_mutex> lock(mutex_);
842 48x wrapper_list_.push_back(wrapper);
843 48x }
844
845 48x return wrapper;
846 48x }
847
848 inline void
849 48x win_udp_service::destroy(io_object::implementation* p)
850 {
851
1/2
✓ Branch 2 → 3 taken 48 times.
✗ Branch 2 → 5 not taken.
48x if (p)
852 {
853 48x auto& wrapper = static_cast<win_udp_socket&>(*p);
854 48x wrapper.close_internal();
855 48x destroy_impl(wrapper);
856 }
857 48x }
858
859 inline void
860 93x win_udp_service::close(io_object::handle& h)
861 {
862 93x auto& wrapper = static_cast<win_udp_socket&>(*h.get());
863 93x wrapper.get_internal()->close_socket();
864 93x }
865
866 inline void
867 48x win_udp_service::destroy_impl(win_udp_socket& impl)
868 {
869 {
870 48x std::lock_guard<win_mutex> lock(mutex_);
871 48x wrapper_list_.remove(&impl);
872 48x }
873
1/2
✓ Branch 5 → 6 taken 48 times.
✗ Branch 5 → 7 not taken.
48x delete &impl;
874 48x }
875
876 inline void
877 48x win_udp_service::unregister_impl(win_udp_socket_internal& impl)
878 {
879 48x std::lock_guard<win_mutex> lock(mutex_);
880 48x socket_list_.remove(&impl);
881 48x }
882
883 inline std::error_code
884 45x win_udp_service::open_socket(
885 win_udp_socket_internal& impl, int family, int type, int protocol)
886 {
887 45x impl.close_socket();
888
889 SOCKET sock =
890 45x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
891
892
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 45 times.
45x if (sock == INVALID_SOCKET)
893 return make_err(::WSAGetLastError());
894
895
2/2
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 10 taken 38 times.
45x if (family == AF_INET6)
896 {
897 7x DWORD one = 1;
898
1/1
✓ Branch 8 → 9 taken 7 times.
7x ::setsockopt(
899 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
900 sizeof(one));
901 }
902
903 90x HANDLE result = ::CreateIoCompletionPort(
904 45x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
905
906
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 45 times.
45x if (result == nullptr)
907 {
908 DWORD dwError = ::GetLastError();
909 ::closesocket(sock);
910 return make_err(dwError);
911 }
912
913 45x impl.socket_ = sock;
914 45x impl.family_ = family;
915 45x return {};
916 }
917
918 inline std::error_code
919 45x win_udp_service::open_datagram_socket(
920 udp_socket::implementation& impl, int family, int type, int protocol)
921 {
922 45x auto& wrapper = static_cast<win_udp_socket&>(impl);
923 45x return open_socket(*wrapper.get_internal(), family, type, protocol);
924 }
925
926 inline std::error_code
927 25x win_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
928 {
929 25x auto& wrapper = static_cast<win_udp_socket&>(impl);
930 25x auto* internal = wrapper.get_internal();
931 25x SOCKET sock = internal->socket_;
932
933 25x sockaddr_storage storage{};
934 25x socklen_t addrlen = detail::to_sockaddr(ep, storage);
935
1/1
✓ Branch 4 → 5 taken 25 times.
25x if (::bind(
936 sock, reinterpret_cast<sockaddr*>(&storage),
937
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 25 times.
25x static_cast<int>(addrlen)) == SOCKET_ERROR)
938 return make_err(::WSAGetLastError());
939
940 // Cache local endpoint (resolves ephemeral port)
941 25x sockaddr_storage local_storage{};
942 25x int local_len = sizeof(local_storage);
943
1/1
✓ Branch 8 → 9 taken 25 times.
25x if (::getsockname(
944
1/2
✓ Branch 9 → 10 taken 25 times.
✗ Branch 9 → 12 not taken.
25x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
945 25x internal->local_endpoint_ = detail::from_sockaddr(local_storage);
946
947 25x return {};
948 }
949
950 inline void
951 win_udp_service::post(overlapped_op* op)
952 {
953 sched_.post(op);
954 }
955
956 inline void
957 36x win_udp_service::on_pending(overlapped_op* op) noexcept
958 {
959 36x sched_.on_pending(op);
960 36x }
961
962 inline void
963 8x win_udp_service::on_completion(
964 overlapped_op* op, DWORD error, DWORD bytes) noexcept
965 {
966 8x sched_.on_completion(op, error, bytes);
967 8x }
968
969 inline void
970 44x win_udp_service::work_started() noexcept
971 {
972 44x sched_.work_started();
973 44x }
974
975 inline void
976 win_udp_service::work_finished() noexcept
977 {
978 sched_.work_finished();
979 }
980
981 } // namespace boost::corosio::detail
982
983 #endif // BOOST_COROSIO_HAS_IOCP
984
985 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_UDP_SERVICE_HPP
986