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

91.2% Lines (427/468) 100.0% List of functions (62/62) 71.9% Branches (123/171)
win_udp_service.hpp
f(x) Functions (62)
Function Calls Lines Branches Blocks
boost::corosio::detail::win_udp_service::scheduler() :83 350x 100.0% 100.0% boost::corosio::detail::send_to_op::send_to_op(boost::corosio::detail::win_udp_socket_internal&) :98 90x 100.0% 100.0% boost::corosio::detail::recv_from_op::recv_from_op(boost::corosio::detail::win_udp_socket_internal&) :105 90x 100.0% 100.0% boost::corosio::detail::udp_connect_op::udp_connect_op(boost::corosio::detail::win_udp_socket_internal&) :112 90x 100.0% 100.0% boost::corosio::detail::udp_send_op::udp_send_op(boost::corosio::detail::win_udp_socket_internal&) :120 90x 100.0% 100.0% boost::corosio::detail::udp_recv_op::udp_recv_op(boost::corosio::detail::win_udp_socket_internal&) :127 90x 100.0% 100.0% boost::corosio::detail::udp_wait_op::udp_wait_op(boost::corosio::detail::win_udp_socket_internal&) :134 90x 100.0% 100.0% boost::corosio::detail::send_to_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :144 1x 100.0% 50.0% 88.9% 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 17x 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 1x 100.0% 100.0% boost::corosio::detail::udp_send_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :229 1x 100.0% 50.0% 88.9% 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 2x 100.0% 50.0% 90.9% boost::corosio::detail::udp_connect_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :268 13x 83.3% 75.0% 76.0% boost::corosio::detail::udp_send_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :302 7x 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 90x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::~win_udp_socket_internal() :375 90x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::native_handle() const :381 529x 100.0% 100.0% boost::corosio::detail::win_udp_socket_internal::local_endpoint() const :387 27x 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 7x 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 17x 100.0% 91.7% 96.9% 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 94.4% 85.0% 88.1% 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 13x 94.1% 60.0% 83.3% 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 7x 85.2% 66.7% 77.4% 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 84.4% 75.0% 76.2% 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 86.7% 66.7% 78.9% 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 338x 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 90x 100.0% 100.0% boost::corosio::detail::win_udp_socket::close_internal() :770 90x 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 17x 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 13x 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 7x 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 454x 100.0% 100.0% boost::corosio::detail::win_udp_socket::set_option(int, int, void const*, unsigned long long) :862 42x 100.0% 100.0% 100.0% boost::corosio::detail::win_udp_socket::get_option(int, int, void*, unsigned long long*) const :873 26x 87.5% 50.0% 77.8% boost::corosio::detail::win_udp_socket::local_endpoint() const :886 27x 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 289x 100.0% 100.0% boost::corosio::detail::win_udp_service::win_udp_service(boost::capy::execution_context&) :911 598x 100.0% 100.0% 75.0% boost::corosio::detail::win_udp_service::~win_udp_service() :917 1196x 100.0% 100.0% boost::corosio::detail::win_udp_service::shutdown() :925 598x 66.7% 50.0% 71.4% boost::corosio::detail::win_udp_service::construct() :937 90x 100.0% 100.0% 89.5% boost::corosio::detail::win_udp_service::destroy(boost::corosio::io_object::implementation*) :957 90x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::close(boost::corosio::io_object::handle&) :968 169x 100.0% 100.0% boost::corosio::detail::win_udp_service::destroy_impl(boost::corosio::detail::win_udp_socket&) :975 90x 100.0% 50.0% 100.0% boost::corosio::detail::win_udp_service::unregister_impl(boost::corosio::detail::win_udp_socket_internal&) :985 90x 100.0% 100.0% boost::corosio::detail::win_udp_service::open_socket(boost::corosio::detail::win_udp_socket_internal&, int, int, int) :992 79x 76.5% 71.4% 68.8% boost::corosio::detail::win_udp_service::open_datagram_socket(boost::corosio::udp_socket::implementation&, int, int, int) :1027 79x 100.0% 100.0% boost::corosio::detail::win_udp_service::bind_datagram(boost::corosio::udp_socket::implementation&, boost::corosio::endpoint) :1035 41x 100.0% 85.7% 100.0% boost::corosio::detail::win_udp_service::on_pending(boost::corosio::detail::overlapped_op*) :1065 50x 100.0% 100.0% boost::corosio::detail::win_udp_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :1071 15x 100.0% 100.0% boost::corosio::detail::win_udp_service::work_started() :1078 71x 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 350x win_scheduler& scheduler() noexcept
84 {
85 350x 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 90x inline send_to_op::send_to_op(win_udp_socket_internal& internal_) noexcept
99 : overlapped_op(&do_complete)
100 90x , internal(internal_)
101 {
102 90x cancel_func_ = &do_cancel_impl;
103 90x }
104
105 90x inline recv_from_op::recv_from_op(win_udp_socket_internal& internal_) noexcept
106 : overlapped_op(&do_complete)
107 90x , internal(internal_)
108 {
109 90x cancel_func_ = &do_cancel_impl;
110 90x }
111
112 90x inline udp_connect_op::udp_connect_op(
113 90x win_udp_socket_internal& internal_) noexcept
114 : overlapped_op(&do_complete)
115 90x , internal(internal_)
116 {
117 90x cancel_func_ = &do_cancel_impl;
118 90x }
119
120 90x inline udp_send_op::udp_send_op(win_udp_socket_internal& internal_) noexcept
121 : overlapped_op(&do_complete)
122 90x , internal(internal_)
123 {
124 90x cancel_func_ = &do_cancel_impl;
125 90x }
126
127 90x inline udp_recv_op::udp_recv_op(win_udp_socket_internal& internal_) noexcept
128 : overlapped_op(&do_complete)
129 90x , internal(internal_)
130 {
131 90x cancel_func_ = &do_cancel_impl;
132 90x }
133
134 90x inline udp_wait_op::udp_wait_op(win_udp_socket_internal& internal_) noexcept
135 : overlapped_op(&do_complete)
136 90x , internal(internal_)
137 {
138 90x cancel_func_ = &do_cancel_impl;
139 90x }
140
141 // Cancellation functions
142
143 inline void
144 1x send_to_op::do_cancel_impl(overlapped_op* base) noexcept
145 {
146 1x auto* op = static_cast<send_to_op*>(base);
147 1x op->cancelled.store(true, std::memory_order_release);
148
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
149 {
150
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
151 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
152 }
153 1x }
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 17x send_to_op::do_complete(
171 void* owner,
172 scheduler_op* base,
173 std::uint32_t /*bytes*/,
174 std::uint32_t /*error*/)
175 {
176 17x auto* op = static_cast<send_to_op*>(base);
177
178
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 17 times.
17x if (!owner)
179 {
180 op->cleanup_only();
181 op->internal_ptr.reset();
182 return;
183 }
184
185 17x auto prevent_premature_destruction = std::move(op->internal_ptr);
186
1/1
✓ Branch 8 → 9 taken 17 times.
17x op->invoke_handler();
187 17x }
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 1x udp_connect_op::do_cancel_impl(overlapped_op* base) noexcept
223 {
224 1x auto* op = static_cast<udp_connect_op*>(base);
225 1x op->cancelled.store(true, std::memory_order_release);
226 1x }
227
228 inline void
229 1x udp_send_op::do_cancel_impl(overlapped_op* base) noexcept
230 {
231 1x auto* op = static_cast<udp_send_op*>(base);
232 1x op->cancelled.store(true, std::memory_order_release);
233
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 10 not taken.
1x if (op->internal.is_open())
234 {
235
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
2x ::CancelIoEx(
236 1x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
237 }
238 1x }
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 2x udp_wait_op::do_cancel_impl(overlapped_op* base) noexcept
254 {
255 2x auto* op = static_cast<udp_wait_op*>(base);
256 2x op->cancelled.store(true, std::memory_order_release);
257
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 10 not taken.
2x if (op->internal.is_open())
258 {
259
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
4x ::CancelIoEx(
260 2x reinterpret_cast<HANDLE>(op->internal.native_handle()), op);
261 }
262 2x op->internal.svc_.scheduler().cancel_wait_if_constructed(op);
263 2x }
264
265 // Connected-mode completion handlers
266
267 inline void
268 13x udp_connect_op::do_complete(
269 void* owner,
270 scheduler_op* base,
271 std::uint32_t /*bytes*/,
272 std::uint32_t /*error*/)
273 {
274 13x auto* op = static_cast<udp_connect_op*>(base);
275
276
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 13 times.
13x 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
3/4
✓ Branch 6 → 7 taken 13 times.
✗ Branch 6 → 10 not taken.
✓ Branch 8 → 9 taken 12 times.
✓ Branch 8 → 10 taken 1 time.
13x (op->dwError == 0 && !op->cancelled.load(std::memory_order_acquire));
286
2/2
✓ Branch 11 → 12 taken 12 times.
✓ Branch 11 → 17 taken 1 time.
13x 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 13x auto prevent_premature_destruction = std::move(op->internal_ptr);
298
1/1
✓ Branch 19 → 20 taken 13 times.
13x op->invoke_handler();
299 13x }
300
301 inline void
302 7x udp_send_op::do_complete(
303 void* owner,
304 scheduler_op* base,
305 std::uint32_t /*bytes*/,
306 std::uint32_t /*error*/)
307 {
308 7x auto* op = static_cast<udp_send_op*>(base);
309
310
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 7 times.
7x if (!owner)
311 {
312 op->cleanup_only();
313 op->internal_ptr.reset();
314 return;
315 }
316
317 7x auto prevent_premature_destruction = std::move(op->internal_ptr);
318
1/1
✓ Branch 8 → 9 taken 7 times.
7x op->invoke_handler();
319 7x }
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 90x inline win_udp_socket_internal::win_udp_socket_internal(
364 90x win_udp_service& svc) noexcept
365 90x : svc_(svc)
366 90x , wr_(*this)
367 90x , rd_(*this)
368 90x , conn_(*this)
369 90x , send_wr_(*this)
370 90x , recv_rd_(*this)
371 180x , wt_(*this)
372 {
373 90x }
374
375 90x inline win_udp_socket_internal::~win_udp_socket_internal()
376 {
377 90x svc_.unregister_impl(*this);
378 90x }
379
380 inline SOCKET
381 529x win_udp_socket_internal::native_handle() const noexcept
382 {
383 529x return socket_;
384 }
385
386 inline endpoint
387 27x win_udp_socket_internal::local_endpoint() const noexcept
388 {
389 27x 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 7x win_udp_socket_internal::is_open() const noexcept
400 {
401 7x return socket_ != INVALID_SOCKET;
402 }
403
404 inline std::coroutine_handle<>
405 17x 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 17 times.
17x wr_.internal_ptr = shared_from_this();
417
418 17x auto& op = wr_;
419 17x op.reset();
420 17x op.h = h;
421 17x op.ex = d;
422 17x op.ec_out = ec;
423 17x op.bytes_out = bytes_out;
424 17x op.start(token);
425
426 17x svc_.work_started();
427
428 // Prepare buffers
429 17x capy::mutable_buffer bufs[send_to_op::max_buffers];
430 17x op.wsabuf_count =
431 17x static_cast<DWORD>(param.copy_to(bufs, send_to_op::max_buffers));
432
433
2/2
✓ Branch 13 → 10 taken 16 times.
✓ Branch 13 → 14 taken 17 times.
33x for (DWORD i = 0; i < op.wsabuf_count; ++i)
434 {
435 16x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
436 16x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
437 }
438
439 // Prepare destination address
440 17x op.dest_len = static_cast<int>(to_sockaddr(dest, family_, op.dest_storage));
441
442 34x int result = ::WSASendTo(
443 17x socket_, op.wsabufs, op.wsabuf_count, nullptr,
444 static_cast<DWORD>(flags),
445
1/1
✓ Branch 15 → 16 taken 17 times.
17x reinterpret_cast<sockaddr*>(&op.dest_storage), op.dest_len, &op,
446 nullptr);
447
448
2/2
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 23 taken 16 times.
17x if (result == SOCKET_ERROR)
449 {
450
1/1
✓ Branch 17 → 18 taken 1 time.
1x DWORD err = ::WSAGetLastError();
451
1/2
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 23 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 16x svc_.on_pending(&op);
459
460 // Re-check cancellation after I/O is pending
461
2/2
✓ Branch 25 → 26 taken 1 time.
✓ Branch 25 → 27 taken 15 times.
16x if (op.cancelled.load(std::memory_order_acquire))
462
1/1
✓ Branch 26 → 27 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
463
464 16x 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 9 → 10 taken 22 times.
✓ Branch 9 → 13 taken 1 time.
✓ Branch 10 → 11 taken 22 times.
✗ Branch 10 → 14 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 22 times.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 20 taken 22 times.
23x 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 24 → 21 taken 22 times.
✓ Branch 24 → 25 taken 22 times.
44x for (DWORD i = 0; i < op.wsabuf_count; ++i)
506 {
507 22x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
508 22x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
509 }
510
511 22x op.flags = static_cast<DWORD>(flags);
512 22x std::memset(&op.source_storage, 0, sizeof(op.source_storage));
513 22x op.source_len = sizeof(op.source_storage);
514
515 44x int result = ::WSARecvFrom(
516 22x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags,
517
1/1
✓ Branch 25 → 26 taken 22 times.
22x reinterpret_cast<sockaddr*>(&op.source_storage), &op.source_len, &op,
518 nullptr);
519
520
2/2
✓ Branch 26 → 27 taken 8 times.
✓ Branch 26 → 33 taken 14 times.
22x if (result == SOCKET_ERROR)
521 {
522
1/1
✓ Branch 27 → 28 taken 8 times.
8x DWORD err = ::WSAGetLastError();
523
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 33 taken 8 times.
8x if (err != WSA_IO_PENDING)
524 {
525 svc_.on_completion(&op, err, 0);
526 return std::noop_coroutine();
527 }
528 }
529
530 22x svc_.on_pending(&op);
531
532 // Re-check cancellation after I/O is pending
533
2/2
✓ Branch 35 → 36 taken 1 time.
✓ Branch 35 → 37 taken 21 times.
22x if (op.cancelled.load(std::memory_order_acquire))
534
1/1
✓ Branch 36 → 37 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
535
536 22x return std::noop_coroutine();
537 }
538
539 // UDP connect is synchronous on Windows
540 inline std::coroutine_handle<>
541 13x 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 13 times.
13x conn_.internal_ptr = shared_from_this();
549
550 13x auto& op = conn_;
551 13x op.reset();
552 13x op.h = h;
553 13x op.ex = d;
554 13x op.ec_out = ec;
555 13x op.target_endpoint = ep;
556 13x op.start(token);
557
558 13x svc_.work_started();
559
560 13x sockaddr_storage storage{};
561 13x socklen_t addrlen = detail::to_sockaddr(ep, storage);
562
1/1
✓ Branch 9 → 10 taken 13 times.
13x int result = ::WSAConnect(
563 socket_, reinterpret_cast<sockaddr*>(&storage),
564 static_cast<int>(addrlen), nullptr, nullptr, nullptr, nullptr);
565
566
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 13 times.
13x if (result == SOCKET_ERROR)
567 svc_.on_completion(&op, ::WSAGetLastError(), 0);
568 else
569 13x svc_.on_completion(&op, 0, 0);
570
571 13x return std::noop_coroutine();
572 }
573
574 inline std::coroutine_handle<>
575 7x 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 7 times.
7x send_wr_.internal_ptr = shared_from_this();
585
586 7x auto& op = send_wr_;
587 7x op.reset();
588 7x op.h = h;
589 7x op.ex = d;
590 7x op.ec_out = ec;
591 7x op.bytes_out = bytes_out;
592 7x op.start(token);
593
594 7x svc_.work_started();
595
596 7x capy::mutable_buffer bufs[udp_send_op::max_buffers];
597 7x op.wsabuf_count =
598 7x static_cast<DWORD>(param.copy_to(bufs, udp_send_op::max_buffers));
599
600
2/2
✓ Branch 13 → 10 taken 7 times.
✓ Branch 13 → 14 taken 7 times.
14x for (DWORD i = 0; i < op.wsabuf_count; ++i)
601 {
602 7x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
603 7x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
604 }
605
606 14x int result = ::WSASend(
607
1/1
✓ Branch 14 → 15 taken 7 times.
7x socket_, op.wsabufs, op.wsabuf_count, nullptr,
608 static_cast<DWORD>(flags), &op, nullptr);
609
610
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 22 taken 7 times.
7x 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 7x svc_.on_pending(&op);
621
622
2/2
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 26 taken 6 times.
7x if (op.cancelled.load(std::memory_order_acquire))
623
1/1
✓ Branch 25 → 26 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
624
625 7x 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
4/8
✓ Branch 9 → 10 taken 5 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 5 times.
✗ Branch 10 → 14 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 5 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 20 taken 5 times.
5x 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 24 → 21 taken 5 times.
✓ Branch 24 → 25 taken 5 times.
10x for (DWORD i = 0; i < op.wsabuf_count; ++i)
662 {
663 5x op.wsabufs[i].buf = static_cast<char*>(bufs[i].data());
664 5x op.wsabufs[i].len = static_cast<ULONG>(bufs[i].size());
665 }
666
667 5x op.flags = static_cast<DWORD>(flags);
668
669 10x int result = ::WSARecv(
670
1/1
✓ Branch 25 → 26 taken 5 times.
5x socket_, op.wsabufs, op.wsabuf_count, nullptr, &op.flags, &op, nullptr);
671
672
2/2
✓ Branch 26 → 27 taken 3 times.
✓ Branch 26 → 33 taken 2 times.
5x if (result == SOCKET_ERROR)
673 {
674
1/1
✓ Branch 27 → 28 taken 3 times.
3x DWORD err = ::WSAGetLastError();
675
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 33 taken 3 times.
3x if (err != WSA_IO_PENDING)
676 {
677 svc_.on_completion(&op, err, 0);
678 return std::noop_coroutine();
679 }
680 }
681
682 5x svc_.on_pending(&op);
683
684
2/2
✓ Branch 35 → 36 taken 1 time.
✓ Branch 35 → 37 taken 4 times.
5x if (op.cancelled.load(std::memory_order_acquire))
685
1/1
✓ Branch 36 → 37 taken 1 time.
1x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), &op);
686
687 5x 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
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 13 taken 6 times.
6x 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 6x svc_.scheduler().wait_reactor().register_wait(socket_, w, &op);
722 6x 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 338x win_udp_socket_internal::close_socket() noexcept
744 {
745 338x wt_.request_cancel();
746 338x svc_.scheduler().cancel_wait_if_constructed(&wt_);
747
748
2/2
✓ Branch 5 → 6 taken 79 times.
✓ Branch 5 → 9 taken 259 times.
338x if (socket_ != INVALID_SOCKET)
749 {
750 79x ::CancelIoEx(reinterpret_cast<HANDLE>(socket_), nullptr);
751 79x ::closesocket(socket_);
752 79x socket_ = INVALID_SOCKET;
753 }
754
755 338x family_ = AF_UNSPEC;
756
757 338x local_endpoint_ = endpoint{};
758 338x remote_endpoint_ = endpoint{};
759 338x }
760
761 // win_udp_socket
762
763 90x inline win_udp_socket::win_udp_socket(
764 90x std::shared_ptr<win_udp_socket_internal> internal) noexcept
765 90x : internal_(std::move(internal))
766 {
767 90x }
768
769 inline void
770 90x win_udp_socket::close_internal() noexcept
771 {
772
1/2
✓ Branch 3 → 4 taken 90 times.
✗ Branch 3 → 7 not taken.
90x if (internal_)
773 {
774 90x internal_->close_socket();
775 90x internal_.reset();
776 }
777 90x }
778
779 inline std::coroutine_handle<>
780 17x 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 17 times.
17x 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 13x 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 13 times.
13x return internal_->connect(h, d, ep, token, ec);
816 }
817
818 inline std::coroutine_handle<>
819 7x 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 7 times.
7x 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 454x win_udp_socket::native_handle() const noexcept
857 {
858 454x return static_cast<native_handle_type>(internal_->native_handle());
859 }
860
861 inline std::error_code
862 42x win_udp_socket::set_option(
863 int level, int optname, void const* data, std::size_t size) noexcept
864 {
865 42x if (::setsockopt(
866 42x internal_->native_handle(), level, optname,
867
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 40 times.
42x reinterpret_cast<char const*>(data), static_cast<int>(size)) != 0)
868 2x return make_err(WSAGetLastError());
869 40x return {};
870 }
871
872 inline std::error_code
873 26x win_udp_socket::get_option(
874 int level, int optname, void* data, std::size_t* size) const noexcept
875 {
876 26x int len = static_cast<int>(*size);
877 26x if (::getsockopt(
878 26x internal_->native_handle(), level, optname,
879
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 26 times.
26x reinterpret_cast<char*>(data), &len) != 0)
880 return make_err(WSAGetLastError());
881 26x *size = static_cast<std::size_t>(len);
882 26x return {};
883 }
884
885 inline endpoint
886 27x win_udp_socket::local_endpoint() const noexcept
887 {
888 27x 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 289x win_udp_socket::get_internal() const noexcept
905 {
906 289x return internal_.get();
907 }
908
909 // win_udp_service
910
911 598x inline win_udp_service::win_udp_service(capy::execution_context& ctx)
912 1196x : sched_(ctx.use_service<win_scheduler>())
913
2/2
✓ Branch 4 → 5 taken 598 times.
✓ Branch 5 → 6 taken 598 times.
598x , iocp_(sched_.native_handle())
914 {
915 598x }
916
917 1196x inline win_udp_service::~win_udp_service()
918 {
919
1/2
✗ Branch 6 → 3 not taken.
✓ Branch 6 → 7 taken 598 times.
598x for (auto* w = wrapper_list_.pop_front(); w != nullptr;
920 w = wrapper_list_.pop_front())
921 delete w;
922 1196x }
923
924 inline void
925 598x win_udp_service::shutdown()
926 {
927 598x std::lock_guard<win_mutex> lock(mutex_);
928
929
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 598 times.
598x for (auto* impl = socket_list_.pop_front(); impl != nullptr;
930 impl = socket_list_.pop_front())
931 {
932 impl->close_socket();
933 }
934 598x }
935
936 inline io_object::implementation*
937 90x win_udp_service::construct()
938 {
939
1/1
✓ Branch 2 → 3 taken 90 times.
90x auto internal = std::make_shared<win_udp_socket_internal>(*this);
940
941 {
942 90x std::lock_guard<win_mutex> lock(mutex_);
943 90x socket_list_.push_back(internal.get());
944 90x }
945
946
1/1
✓ Branch 7 → 8 taken 90 times.
90x auto* wrapper = new win_udp_socket(std::move(internal));
947
948 {
949 90x std::lock_guard<win_mutex> lock(mutex_);
950 90x wrapper_list_.push_back(wrapper);
951 90x }
952
953 90x return wrapper;
954 90x }
955
956 inline void
957 90x win_udp_service::destroy(io_object::implementation* p)
958 {
959
1/2
✓ Branch 2 → 3 taken 90 times.
✗ Branch 2 → 5 not taken.
90x if (p)
960 {
961 90x auto& wrapper = static_cast<win_udp_socket&>(*p);
962 90x wrapper.close_internal();
963 90x destroy_impl(wrapper);
964 }
965 90x }
966
967 inline void
968 169x win_udp_service::close(io_object::handle& h)
969 {
970 169x auto& wrapper = static_cast<win_udp_socket&>(*h.get());
971 169x wrapper.get_internal()->close_socket();
972 169x }
973
974 inline void
975 90x win_udp_service::destroy_impl(win_udp_socket& impl)
976 {
977 {
978 90x std::lock_guard<win_mutex> lock(mutex_);
979 90x wrapper_list_.remove(&impl);
980 90x }
981
1/2
✓ Branch 5 → 6 taken 90 times.
✗ Branch 5 → 7 not taken.
90x delete &impl;
982 90x }
983
984 inline void
985 90x win_udp_service::unregister_impl(win_udp_socket_internal& impl)
986 {
987 90x std::lock_guard<win_mutex> lock(mutex_);
988 90x socket_list_.remove(&impl);
989 90x }
990
991 inline std::error_code
992 79x win_udp_service::open_socket(
993 win_udp_socket_internal& impl, int family, int type, int protocol)
994 {
995 79x impl.close_socket();
996
997 SOCKET sock =
998 79x ::WSASocketW(family, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
999
1000
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 79 times.
79x if (sock == INVALID_SOCKET)
1001 return make_err(::WSAGetLastError());
1002
1003
2/2
✓ Branch 7 → 8 taken 11 times.
✓ Branch 7 → 10 taken 68 times.
79x if (family == AF_INET6)
1004 {
1005 11x DWORD one = 1;
1006
1/1
✓ Branch 8 → 9 taken 11 times.
11x ::setsockopt(
1007 sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&one),
1008 sizeof(one));
1009 }
1010
1011 158x HANDLE result = ::CreateIoCompletionPort(
1012 79x reinterpret_cast<HANDLE>(sock), static_cast<HANDLE>(iocp_), key_io, 0);
1013
1014
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 79 times.
79x if (result == nullptr)
1015 {
1016 DWORD dwError = ::GetLastError();
1017 ::closesocket(sock);
1018 return make_err(dwError);
1019 }
1020
1021 79x impl.socket_ = sock;
1022 79x impl.family_ = family;
1023 79x return {};
1024 }
1025
1026 inline std::error_code
1027 79x win_udp_service::open_datagram_socket(
1028 udp_socket::implementation& impl, int family, int type, int protocol)
1029 {
1030 79x auto& wrapper = static_cast<win_udp_socket&>(impl);
1031 79x return open_socket(*wrapper.get_internal(), family, type, protocol);
1032 }
1033
1034 inline std::error_code
1035 41x win_udp_service::bind_datagram(udp_socket::implementation& impl, endpoint ep)
1036 {
1037 41x auto& wrapper = static_cast<win_udp_socket&>(impl);
1038 41x auto* internal = wrapper.get_internal();
1039 41x SOCKET sock = internal->socket_;
1040
1041 41x sockaddr_storage storage{};
1042 41x socklen_t addrlen = detail::to_sockaddr(ep, storage);
1043
1/1
✓ Branch 4 → 5 taken 41 times.
41x if (::bind(
1044 sock, reinterpret_cast<sockaddr*>(&storage),
1045
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 39 times.
41x 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 39x sockaddr_storage local_storage{};
1050 39x int local_len = sizeof(local_storage);
1051
1/1
✓ Branch 8 → 9 taken 39 times.
39x if (::getsockname(
1052
1/2
✓ Branch 9 → 10 taken 39 times.
✗ Branch 9 → 12 not taken.
39x sock, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
1053 39x internal->local_endpoint_ = detail::from_sockaddr(local_storage);
1054
1055 39x 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 50x win_udp_service::on_pending(overlapped_op* op) noexcept
1066 {
1067 50x sched_.on_pending(op);
1068 50x }
1069
1070 inline void
1071 15x win_udp_service::on_completion(
1072 overlapped_op* op, DWORD error, DWORD bytes) noexcept
1073 {
1074 15x sched_.on_completion(op, error, bytes);
1075 15x }
1076
1077 inline void
1078 71x win_udp_service::work_started() noexcept
1079 {
1080 71x sched_.work_started();
1081 71x }
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