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

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