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

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