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

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