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

86.8% Lines (406/468) 93.5% List of functions (58/62) 66.1% Branches (113/171)
win_udp_service.hpp
f(x) Functions (62)
Function Calls Lines Branches Blocks
boost::corosio::detail::win_udp_service::scheduler() :83 352x 100.0% 100.0% boost::corosio::detail::send_to_op::send_to_op(boost::corosio::detail::win_udp_socket_internal&) :98 91x 100.0% 100.0% boost::corosio::detail::recv_from_op::recv_from_op(boost::corosio::detail::win_udp_socket_internal&) :105 91x 100.0% 100.0% boost::corosio::detail::udp_connect_op::udp_connect_op(boost::corosio::detail::win_udp_socket_internal&) :112 91x 100.0% 100.0% boost::corosio::detail::udp_send_op::udp_send_op(boost::corosio::detail::win_udp_socket_internal&) :120 91x 100.0% 100.0% boost::corosio::detail::udp_recv_op::udp_recv_op(boost::corosio::detail::win_udp_socket_internal&) :127 91x 100.0% 100.0% boost::corosio::detail::udp_wait_op::udp_wait_op(boost::corosio::detail::win_udp_socket_internal&) :134 91x 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 2x 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 16x 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 23x 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 1x 100.0% 50.0% 88.9% 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 12x 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 6x 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 5x 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 6x 66.7% 66.7% 61.5% boost::corosio::detail::win_udp_socket_internal::win_udp_socket_internal(boost::corosio::detail::win_udp_service&) :363 91x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::~win_udp_socket_internal() :375 91x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::native_handle() const :381 492x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::local_endpoint() const :387 26x 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 3x 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 16x 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 23x 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 12x 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 6x 96.3% 75.0% 93.9% 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 5x 90.6% 75.0% 86.4% 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 6x 100.0% 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::cancel() :726 5x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_socket_internal::close_socket() :743 342x 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 91x 100.0% 100.0% boost::corosio::detail::win_udp_socket::close_internal() :770 91x 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 16x 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 23x 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 12x 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 6x 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 5x 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 6x 100.0% 100.0% 80.0% boost::corosio::detail::win_udp_socket::native_handle() const :856 438x 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 26x 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 5x 100.0% 100.0% boost::corosio::detail::win_udp_socket::get_internal() const :904 297x 100.0% 100.0% boost::corosio::detail::win_udp_service::win_udp_service(boost::capy::execution_context&) :911 594x 100.0% 100.0% 75.0% boost::corosio::detail::win_udp_service::~win_udp_service() :917 1188x 100.0% 100.0% boost::corosio::detail::win_udp_service::shutdown() :925 594x 66.7% 50.0% 71.4% boost::corosio::detail::win_udp_service::construct() :937 91x 100.0% 100.0% 89.5% boost::corosio::detail::win_udp_service::destroy(boost::corosio::io_object::implementation*) :957 91x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::close(boost::corosio::io_object::handle&) :968 171x 100.0% 100.0% boost::corosio::detail::win_udp_service::destroy_impl(boost::corosio::detail::win_udp_socket&) :975 91x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::unregister_impl(boost::corosio::detail::win_udp_socket_internal&) :985 91x 100.0% 100.0% boost::corosio::detail::win_udp_service::open_socket(boost::corosio::detail::win_udp_socket_internal&, int, int, int) :992 80x 76.5% 71.4% 68.8% boost::corosio::detail::win_udp_service::open_datagram_socket(boost::corosio::udp_socket::implementation&, int, int, int) :1027 80x 100.0% 100.0% boost::corosio::detail::win_udp_service::bind_datagram(boost::corosio::udp_socket::implementation&, boost::corosio::endpoint) :1035 46x 100.0% 85.7% 100.0% boost::corosio::detail::win_udp_service::on_pending(boost::corosio::detail::overlapped_op*) :1065 44x 100.0% 100.0% boost::corosio::detail::win_udp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1071 19x 100.0% 100.0% boost::corosio::detail::win_udp_service::work_started() :1078 68x 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 352x win_scheduler& scheduler() noexcept
84 {
85 352x 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 91x inline send_to_op::send_to_op(win_udp_socket_internal& internal_) noexcept
99 : overlapped_op(&do_complete)
100 91x , internal(internal_)
101 {
102 91x cancel_func_ = &do_cancel_impl;
103 91x }
104
105 91x inline recv_from_op::recv_from_op(win_udp_socket_internal& internal_) noexcept
106 : overlapped_op(&do_complete)
107 91x , internal(internal_)
108 {
109 91x cancel_func_ = &do_cancel_impl;
110 91x }
111
112 91x inline udp_connect_op::udp_connect_op(
113 91x win_udp_socket_internal& internal_) noexcept
114 : overlapped_op(&do_complete)
115 91x , internal(internal_)
116 {
117 91x cancel_func_ = &do_cancel_impl;
118 91x }
119
120 91x inline udp_send_op::udp_send_op(win_udp_socket_internal& internal_) noexcept
121 : overlapped_op(&do_complete)
122 91x , internal(internal_)
123 {
124 91x cancel_func_ = &do_cancel_impl;
125 91x }
126
127 91x inline udp_recv_op::udp_recv_op(win_udp_socket_internal& internal_) noexcept
128 : overlapped_op(&do_complete)
129 91x , internal(internal_)
130 {
131 91x cancel_func_ = &do_cancel_impl;
132 91x }
133
134 91x inline udp_wait_op::udp_wait_op(win_udp_socket_internal& internal_) noexcept
135 : overlapped_op(&do_complete)
136 91x , internal(internal_)
137 {
138 91x cancel_func_ = &do_cancel_impl;
139 91x }
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 2x recv_from_op::do_cancel_impl(overlapped_op* base) noexcept
157 {
158 2x auto* op = static_cast<recv_from_op*>(base);
159 2x op->cancelled.store(true, std::memory_order_release);
160
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 10 not taken.
2x if (op->internal.is_open())
161 {
162
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
4x ::CancelIoEx(
163 2x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
164 }
165 2x }
166
167 // send_to_op completion handler
168
169 inline void
170 16x send_to_op::do_complete(
171 void* owner,
172 scheduler_op* base,
173 std::uint32_t /*bytes*/,
174 std::uint32_t /*error*/)
175 {
176 16x auto* op = static_cast<send_to_op*>(base);
177
178
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 16 times.
16x if (!owner)
179 {
180 op->cleanup_only();
181 op->internal_ptr.reset();
182 return;
183 }
184
185 16x auto prevent_premature_destruction = std::move(op->internal_ptr);
186
1/1
✓ Branch 8 → 9 taken 16 times.
16x op->invoke_handler();
187 16x }
188
189 // recv_from_op completion handler
190
191 inline void
192 23x recv_from_op::do_complete(
193 void* owner,
194 scheduler_op* base,
195 std::uint32_t /*bytes*/,
196 std::uint32_t /*error*/)
197 {
198 23x auto* op = static_cast<recv_from_op*>(base);
199
200
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 23 times.
23x 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 17 times.
✓ Branch 6 → 10 taken 6 times.
✓ Branch 8 → 9 taken 17 times.
✗ Branch 8 → 10 not taken.
23x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
210
3/4
✓ Branch 11 → 12 taken 17 times.
✓ Branch 11 → 15 taken 6 times.
✓ Branch 12 → 13 taken 17 times.
✗ Branch 12 → 15 not taken.
23x if (success && op->source_out)
211 {
212 17x *op->source_out = from_sockaddr(op->source_storage);
213 }
214
215 23x auto prevent_premature_destruction = std::move(op->internal_ptr);
216
1/1
✓ Branch 17 → 18 taken 23 times.
23x op->invoke_handler();
217 23x }
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 1x udp_recv_op::do_cancel_impl(overlapped_op* base) noexcept
242 {
243 1x auto* op = static_cast<udp_recv_op*>(base);
244 1x op->cancelled.store(true, std::memory_order_release);
245
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
246 {
247
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
248 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
249 }
250 1x }
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 12x udp_connect_op::do_complete(
269 void* owner,
270 scheduler_op* base,
271 std::uint32_t /*bytes*/,
272 std::uint32_t /*error*/)
273 {
274 12x auto* op = static_cast<udp_connect_op*>(base);
275
276
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 12 times.
12x 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 12 times.
✗ Branch 6 → 10 not taken.
✓ Branch 8 → 9 taken 12 times.
✗ Branch 8 → 10 not taken.
12x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
286
1/2
✓ Branch 11 → 12 taken 12 times.
✗ Branch 11 → 17 not taken.
12x if (success)
287 {
288 12x sockaddr_storage local_storage{};
289 12x int local_len = sizeof(local_storage);
290 24x if (::getsockname(
291
1/1
✓ Branch 12 → 13 taken 12 times.
12x op->internal.socket_,
292
1/2
✓ Branch 13 → 14 taken 12 times.
✗ Branch 13 → 16 not taken.
12x reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
293 12x op->internal.local_endpoint_ = from_sockaddr(local_storage);
294 12x op->internal.remote_endpoint_ = op->target_endpoint;
295 }
296
297 12x auto prevent_premature_destruction = std::move(op->internal_ptr);
298
1/1
✓ Branch 19 → 20 taken 12 times.
12x op->invoke_handler();
299 12x }
300
301 inline void
302 6x udp_send_op::do_complete(
303 void* owner,
304 scheduler_op* base,
305 std::uint32_t /*bytes*/,
306 std::uint32_t /*error*/)
307 {
308 6x auto* op = static_cast<udp_send_op*>(base);
309
310
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 6 times.
6x if (!owner)
311 {
312 op->cleanup_only();
313 op->internal_ptr.reset();
314 return;
315 }
316
317 6x auto prevent_premature_destruction = std::move(op->internal_ptr);
318
1/1
✓ Branch 8 → 9 taken 6 times.
6x op->invoke_handler();
319 6x }
320
321 inline void
322 5x udp_recv_op::do_complete(
323 void* owner,
324 scheduler_op* base,
325 std::uint32_t /*bytes*/,
326 std::uint32_t /*error*/)
327 {
328 5x auto* op = static_cast<udp_recv_op*>(base);
329
330
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 5 times.
5x if (!owner)
331 {
332 op->cleanup_only();
333 op->internal_ptr.reset();
334 return;
335 }
336
337 5x auto prevent_premature_destruction = std::move(op->internal_ptr);
338
1/1
✓ Branch 8 → 9 taken 5 times.
5x op->invoke_handler();
339 5x }
340
341 inline void
342 6x udp_wait_op::do_complete(
343 void* owner,
344 scheduler_op* base,
345 std::uint32_t /*bytes*/,
346 std::uint32_t /*error*/)
347 {
348 6x auto* op = static_cast<udp_wait_op*>(base);
349
350
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 6 times.
6x if (!owner)
351 {
352 op->cleanup_only();
353 op->internal_ptr.reset();
354 return;
355 }
356
357 6x auto prevent_premature_destruction = std::move(op->internal_ptr);
358
1/1
✓ Branch 8 → 9 taken 6 times.
6x op->invoke_handler();
359 6x }
360
361 // win_udp_socket_internal
362
363 91x inline win_udp_socket_internal::win_udp_socket_internal(
364 91x win_udp_service& svc) noexcept
365 91x : svc_(svc)
366 91x , wr_(*this)
367 91x , rd_(*this)
368 91x , conn_(*this)
369 91x , send_wr_(*this)
370 91x , recv_rd_(*this)
371 182x , wt_(*this)
372 {
373 91x }
374
375 91x inline win_udp_socket_internal::~win_udp_socket_internal()
376 {
377 91x svc_.unregister_impl(*this);
378 91x }
379
380 inline SOCKET
381 492x win_udp_socket_internal::native_handle() const noexcept
382 {
383 492x return socket_;
384 }
385
386 inline endpoint
387 26x win_udp_socket_internal::local_endpoint() const noexcept
388 {
389 26x 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 3x win_udp_socket_internal::is_open() const noexcept
400 {
401 3x return socket_ != INVALID_SOCKET;
402 }
403
404 inline std::coroutine_handle<>
405 16x 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 16 times.
16x wr_.internal_ptr = shared_from_this();
417
418 16x auto& op = wr_;
419 16x op.reset();
420 16x op.h = h;
421 16x op.ex = d;
422 16x op.ec_out = ec;
423 16x op.bytes_out = bytes_out;
424 16x op.start(token);
425
426 16x svc_.work_started();
427
428 // Prepare buffers
429 16x capy::mutable_buffer bufs[send_to_op::max_buffers];
430 16x op.wsabuf_count =
431 16x 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 16 times.
30x 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 16x op.dest_len = static_cast<int>(to_sockaddr(dest, family_, op.dest_storage));
441
442 32x int result = ::WSASendTo(
443 16x socket_, op.wsabufs, op.wsabuf_count, nullptr,
444 static_cast<DWORD>(flags),
445
1/1
✓ Branch 17 → 18 taken 16 times.
16x reinterpret_cast<sockaddr*>(&op.dest_storage), op.dest_len, &op,
446 nullptr);
447
448
2/2
✓ Branch 18 → 19 taken 2 times.
✓ Branch 18 → 25 taken 14 times.
16x if (result == SOCKET_ERROR)
449 {
450
1/1
✓ Branch 19 → 20 taken 2 times.
2x DWORD err = ::WSAGetLastError();
451
1/2
✓ Branch 20 → 21 taken 2 times.
✗ Branch 20 → 25 not taken.
2x if (err != WSA_IO_PENDING)
452 {
453 2x svc_.on_completion(&op, err, 0);
454 2x 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 23x 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 23 times.
23x rd_.internal_ptr = shared_from_this();
480
481 23x auto& op = rd_;
482 23x op.reset();
483 23x op.h = h;
484 23x op.ex = d;
485 23x op.ec_out = ec;
486 23x op.bytes_out = bytes_out;
487 23x op.source_out = source;
488 23x op.start(token);
489
490 23x svc_.work_started();
491
492 // Prepare buffers
493 23x capy::mutable_buffer bufs[recv_from_op::max_buffers];
494 23x op.wsabuf_count =
495 23x 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 21 times.
✓ Branch 11 → 15 taken 2 times.
✓ Branch 12 → 13 taken 21 times.
✗ Branch 12 → 16 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 21 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 22 taken 21 times.
23x if (op.wsabuf_count == 0 || (op.wsabuf_count == 1 && bufs[0].size() == 0))
499 {
500 2x op.empty_buffer = true;
501 2x svc_.on_completion(&op, 0, 0);
502 2x return std::noop_coroutine();
503 }
504
505
2/2
✓ Branch 26 → 23 taken 21 times.
✓ Branch 26 → 27 taken 21 times.
42x for (DWORD i = 0; i < op.wsabuf_count; ++i)
506 {
507 21x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
508 21x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
509 }
510
511 21x op.flags = static_cast<DWORD>(flags);
512 21x std::memset(&op.source_storage, 0, sizeof(op.source_storage));
513 21x op.source_len = sizeof(op.source_storage);
514
515 42x int result = ::WSARecvFrom(
516 21x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags,
517
1/1
✓ Branch 27 → 28 taken 21 times.
21x reinterpret_cast<sockaddr*>(&op.source_storage), &op.source_len, &op,
518 nullptr);
519
520
2/2
✓ Branch 28 → 29 taken 7 times.
✓ Branch 28 → 35 taken 14 times.
21x if (result == SOCKET_ERROR)
521 {
522
1/1
✓ Branch 29 → 30 taken 7 times.
7x DWORD err = ::WSAGetLastError();
523
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 7 times.
7x if (err != WSA_IO_PENDING)
524 {
525 svc_.on_completion(&op, err, 0);
526 return std::noop_coroutine();
527 }
528 }
529
530 21x 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 21 times.
21x if (op.cancelled.load(std::memory_order_acquire))
534 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
535
536 21x return std::noop_coroutine();
537 }
538
539 // UDP connect is synchronous on Windows
540 inline std::coroutine_handle<>
541 12x 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 12 times.
12x conn_.internal_ptr = shared_from_this();
549
550 12x auto& op = conn_;
551 12x op.reset();
552 12x op.h = h;
553 12x op.ex = d;
554 12x op.ec_out = ec;
555 12x op.target_endpoint = ep;
556 12x op.start(token);
557
558 12x svc_.work_started();
559
560 12x sockaddr_storage storage{};
561 12x socklen_t addrlen = detail::to_sockaddr(ep, storage);
562
1/1
✓ Branch 11 → 12 taken 12 times.
12x 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 12 times.
12x if (result == SOCKET_ERROR)
567 svc_.on_completion(&op, ::WSAGetLastError(), 0);
568 else
569 12x svc_.on_completion(&op, 0, 0);
570
571 12x return std::noop_coroutine();
572 }
573
574 inline std::coroutine_handle<>
575 6x 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 6 times.
6x send_wr_.internal_ptr = shared_from_this();
585
586 6x auto& op = send_wr_;
587 6x op.reset();
588 6x op.h = h;
589 6x op.ex = d;
590 6x op.ec_out = ec;
591 6x op.bytes_out = bytes_out;
592 6x op.start(token);
593
594 6x svc_.work_started();
595
596 6x capy::mutable_buffer bufs[udp_send_op::max_buffers];
597 6x op.wsabuf_count =
598 6x 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 6 times.
11x 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 12x int result = ::WSASend(
607
1/1
✓ Branch 16 → 17 taken 6 times.
6x socket_, op.wsabufs, op.wsabuf_count, nullptr,
608 static_cast<DWORD>(flags), &op, nullptr);
609
610
2/2
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 24 taken 5 times.
6x if (result == SOCKET_ERROR)
611 {
612
1/1
✓ Branch 18 → 19 taken 1 time.
1x DWORD err = ::WSAGetLastError();
613
1/2
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 24 not taken.
1x if (err != WSA_IO_PENDING)
614 {
615 1x svc_.on_completion(&op, err, 0);
616 1x 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 5x 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 5 times.
5x recv_rd_.internal_ptr = shared_from_this();
639
640 5x auto& op = recv_rd_;
641 5x op.reset();
642 5x op.h = h;
643 5x op.ex = d;
644 5x op.ec_out = ec;
645 5x op.bytes_out = bytes_out;
646 5x op.start(token);
647
648 5x svc_.work_started();
649
650 5x capy::mutable_buffer bufs[udp_recv_op::max_buffers];
651 5x op.wsabuf_count =
652 5x static_cast<DWORD>(param.copy_to(bufs, udp_recv_op::max_buffers));
653
654
6/8
✓ Branch 11 → 12 taken 4 times.
✓ Branch 11 → 15 taken 1 time.
✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 16 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 4 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 22 taken 4 times.
5x if (op.wsabuf_count == 0 || (op.wsabuf_count == 1 && bufs[0].size() == 0))
655 {
656 1x op.empty_buffer = true;
657 1x svc_.on_completion(&op, 0, 0);
658 1x return std::noop_coroutine();
659 }
660
661
2/2
✓ Branch 26 → 23 taken 4 times.
✓ Branch 26 → 27 taken 4 times.
8x for (DWORD i = 0; i < op.wsabuf_count; ++i)
662 {
663 4x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
664 4x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
665 }
666
667 4x op.flags = static_cast<DWORD>(flags);
668
669 8x int result = ::WSARecv(
670
1/1
✓ Branch 27 → 28 taken 4 times.
4x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
671
672
2/2
✓ Branch 28 → 29 taken 2 times.
✓ Branch 28 → 35 taken 2 times.
4x if (result == SOCKET_ERROR)
673 {
674
1/1
✓ Branch 29 → 30 taken 2 times.
2x DWORD err = ::WSAGetLastError();
675
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 35 taken 2 times.
2x if (err != WSA_IO_PENDING)
676 {
677 svc_.on_completion(&op, err, 0);
678 return std::noop_coroutine();
679 }
680 }
681
682 4x svc_.on_pending(&op);
683
684
1/2
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 4 times.
4x if (op.cancelled.load(std::memory_order_acquire))
685 ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
686
687 4x return std::noop_coroutine();
688 }
689
690 inline std::coroutine_handle<>
691 6x 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 6 times.
6x wt_.internal_ptr = shared_from_this();
699
700 6x auto& op = wt_;
701 6x op.reset();
702 6x op.h = h;
703 6x op.ex = d;
704 6x op.ec_out = ec;
705 6x op.bytes_out = nullptr;
706 6x op.start(token);
707
708 6x svc_.work_started();
709
710
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 15 taken 5 times.
6x if (w == wait_type::write)
711 {
712 1x svc_.on_completion(&op, 0, 0);
713 1x 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 5x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
722 5x return std::noop_coroutine();
723 }
724
725 inline void
726 5x win_udp_socket_internal::cancel() noexcept
727 {
728
1/2
✓ Branch 2 → 3 taken 5 times.
✗ Branch 2 → 4 not taken.
5x if (socket_ != INVALID_SOCKET)
729 {
730 5x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
731 }
732
733 5x wr_.request_cancel();
734 5x rd_.request_cancel();
735 5x conn_.request_cancel();
736 5x send_wr_.request_cancel();
737 5x recv_rd_.request_cancel();
738 5x wt_.request_cancel();
739 5x svc_.scheduler().cancel_wait_if_constructed(&wt_);
740 5x }
741
742 inline void
743 342x win_udp_socket_internal::close_socket() noexcept
744 {
745 342x wt_.request_cancel();
746 342x svc_.scheduler().cancel_wait_if_constructed(&wt_);
747
748
2/2
✓ Branch 5 → 6 taken 80 times.
✓ Branch 5 → 9 taken 262 times.
342x if (socket_ != INVALID_SOCKET)
749 {
750 80x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
751 80x ::closesocket(socket_);
752 80x socket_ = INVALID_SOCKET;
753 }
754
755 342x family_ = AF_UNSPEC;
756
757 342x local_endpoint_ = endpoint{};
758 342x remote_endpoint_ = endpoint{};
759 342x }
760
761 // win_udp_socket
762
763 91x inline win_udp_socket::win_udp_socket(
764 91x std::shared_ptr<win_udp_socket_internal> internal) noexcept
765 91x : internal_(std::move(internal))
766 {
767 91x }
768
769 inline void
770 91x win_udp_socket::close_internal() noexcept
771 {
772
1/2
✓ Branch 3 → 4 taken 91 times.
✗ Branch 3 → 7 not taken.
91x if (internal_)
773 {
774 91x internal_->close_socket();
775 91x internal_.reset();
776 }
777 91x }
778
779 inline std::coroutine_handle<>
780 16x 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 16 times.
16x return internal_->send_to(h, d, buf, dest, flags, token, ec, bytes);
791 }
792
793 inline std::coroutine_handle<>
794 23x 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 23 times.
23x return internal_->recv_from(h, d, buf, source, flags, token, ec, bytes);
805 }
806
807 inline std::coroutine_handle<>
808 12x 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 12 times.
12x return internal_->connect(h, d, ep, token, ec);
816 }
817
818 inline std::coroutine_handle<>
819 6x 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 6 times.
6x return internal_->send(h, d, buf, flags, token, ec, bytes);
829 }
830
831 inline std::coroutine_handle<>
832 5x 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 5 times.
5x return internal_->recv(h, d, buf, flags, token, ec, bytes);
842 }
843
844 inline std::coroutine_handle<>
845 6x 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 6 times.
6x return internal_->wait(h, d, w, token, ec);
853 }
854
855 inline native_handle_type
856 438x win_udp_socket::native_handle() const noexcept
857 {
858 438x 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 26x win_udp_socket::local_endpoint() const noexcept
887 {
888 26x 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 5x win_udp_socket::cancel() noexcept
899 {
900 5x internal_->cancel();
901 5x }
902
903 inline win_udp_socket_internal*
904 297x win_udp_socket::get_internal() const noexcept
905 {
906 297x return internal_.get();
907 }
908
909 // win_udp_service
910
911 594x inline win_udp_service::win_udp_service(capy::execution_context& ctx)
912 1188x : sched_(ctx.use_service<win_scheduler>())
913
2/2
✓ Branch 4 → 5 taken 594 times.
✓ Branch 5 → 6 taken 594 times.
594x , iocp_(sched_.native_handle())
914 {
915 594x }
916
917 1188x inline win_udp_service::~win_udp_service()
918 {
919
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 594 times.
594x for (auto* w = wrapper_list_.pop_front(); w != nullptr;
920 w = wrapper_list_.pop_front())
921 delete w;
922 1188x }
923
924 inline void
925 594x win_udp_service::shutdown()
926 {
927 594x std::lock_guard<win_mutex> lock(mutex_);
928
929
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 594 times.
594x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
930 impl = socket_list_.pop_front())
931 {
932 impl->close_socket();
933 }
934 594x }
935
936 inline io_object::implementation*
937 91x win_udp_service::construct()
938 {
939
1/1
✓ Branch 2 → 3 taken 91 times.
91x auto internal = std::make_shared<win_udp_socket_internal>(*this);
940
941 {
942 91x std::lock_guard<win_mutex> lock(mutex_);
943 91x socket_list_.push_back(internal.get());
944 91x }
945
946
1/1
✓ Branch 7 → 8 taken 91 times.
91x auto* wrapper = new win_udp_socket(std::move(internal));
947
948 {
949 91x std::lock_guard<win_mutex> lock(mutex_);
950 91x wrapper_list_.push_back(wrapper);
951 91x }
952
953 91x return wrapper;
954 91x }
955
956 inline void
957 91x win_udp_service::destroy(io_object::implementation* p)
958 {
959
1/2
✓ Branch 2 → 3 taken 91 times.
✗ Branch 2 → 5 not taken.
91x if (p)
960 {
961 91x auto& wrapper = static_cast<win_udp_socket&>(*p);
962 91x wrapper.close_internal();
963 91x destroy_impl(wrapper);
964 }
965 91x }
966
967 inline void
968 171x win_udp_service::close(io_object::handle& h)
969 {
970 171x auto& wrapper = static_cast<win_udp_socket&>(*h.get());
971 171x wrapper.get_internal()->close_socket();
972 171x }
973
974 inline void
975 91x win_udp_service::destroy_impl(win_udp_socket& impl)
976 {
977 {
978 91x std::lock_guard<win_mutex> lock(mutex_);
979 91x wrapper_list_.remove(&impl);
980 91x }
981
1/2
✓ Branch 5 → 6 taken 91 times.
✗ Branch 5 → 7 not taken.
91x delete &impl;
982 91x }
983
984 inline void
985 91x win_udp_service::unregister_impl(win_udp_socket_internal& impl)
986 {
987 91x std::lock_guard<win_mutex> lock(mutex_);
988 91x socket_list_.remove(&impl);
989 91x }
990
991 inline std::error_code
992 80x win_udp_service::open_socket(
993 win_udp_socket_internal& impl, int family, int type, int protocol)
994 {
995 80x impl.close_socket();
996
997 SOCKET sock =
998 80x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
999
1000
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 80 times.
80x if (sock == INVALID_SOCKET)
1001 return make_err(::WSAGetLastError());
1002
1003
2/2
✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 10 taken 70 times.
80x 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 160x HANDLE result = ::CreateIoCompletionPort(
1012 80x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1013
1014
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 80 times.
80x if (result == nullptr)
1015 {
1016 DWORD dwError = ::GetLastError();
1017 ::closesocket(sock);
1018 return make_err(dwError);
1019 }
1020
1021 80x impl.socket_ = sock;
1022 80x impl.family_ = family;
1023 80x return {};
1024 }
1025
1026 inline std::error_code
1027 80x win_udp_service::open_datagram_socket(
1028 udp_socket::implementation& impl, int family, int type, int protocol)
1029 {
1030 80x auto& wrapper = static_cast<win_udp_socket&>(impl);
1031 80x return open_socket(*wrapper.get_internal(), family, type, protocol);
1032 }
1033
1034 inline std::error_code
1035 46x win_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
1036 {
1037 46x auto& wrapper = static_cast<win_udp_socket&>(impl);
1038 46x auto* internal = wrapper.get_internal();
1039 46x SOCKET sock = internal->socket_;
1040
1041 46x sockaddr_storage storage{};
1042 46x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1043
1/1
✓ Branch 4 → 5 taken 46 times.
46x if (::bind(
1044 sock, reinterpret_cast<sockaddr*>(&storage),
1045
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 44 times.
46x 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 44x sockaddr_storage local_storage{};
1050 44x int local_len = sizeof(local_storage);
1051
1/1
✓ Branch 8 → 9 taken 44 times.
44x if (::getsockname(
1052
1/2
✓ Branch 9 → 10 taken 44 times.
✗ Branch 9 → 12 not taken.
44x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1053 44x internal->local_endpoint_ = detail::from_sockaddr(local_storage);
1054
1055 44x 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 44x win_udp_service::on_pending(overlapped_op* op) noexcept
1066 {
1067 44x sched_.on_pending(op);
1068 44x }
1069
1070 inline void
1071 19x win_udp_service::on_completion(
1072 overlapped_op* op, DWORD error, DWORD bytes) noexcept
1073 {
1074 19x sched_.on_completion(op, error, bytes);
1075 19x }
1076
1077 inline void
1078 68x win_udp_service::work_started() noexcept
1079 {
1080 68x sched_.work_started();
1081 68x }
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