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

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