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

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