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

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