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

93.3% Lines (56/60) 90.9% Functions (10/11) 86.4% Branches (19/22)
include/boost/corosio/native/detail/iocp/win_overlapped_op.hpp
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
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_OVERLAPPED_OP_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_OVERLAPPED_OP_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/capy/ex/executor_ref.hpp>
20 #include <boost/capy/error.hpp>
21 #include <system_error>
22
23 #include <boost/corosio/detail/make_err.hpp>
24 #include <boost/corosio/detail/dispatch_coro.hpp>
25 #include <boost/corosio/detail/scheduler_op.hpp>
26
27 #include <atomic>
28 #include <coroutine>
29 #include <cstddef>
30 #include <optional>
31 #include <stop_token>
32
33 #include <boost/corosio/native/detail/iocp/win_windows.hpp>
34
35 namespace boost::corosio::detail {
36
37 /** Base class for IOCP overlapped operations.
38
39 Derives from both OVERLAPPED (for Windows IOCP) and scheduler_op
40 (for queueing). Uses function pointer dispatch inherited from
41 scheduler_op - no virtual functions.
42
43 The OVERLAPPED structure is at the start so we can static_cast
44 between OVERLAPPED* and overlapped_op*.
45 */
46 struct overlapped_op
47 : OVERLAPPED
48 , scheduler_op
49 {
50 struct canceller
51 {
52 overlapped_op* op;
53 780 void operator()() const noexcept
54 {
55 780 op->request_cancel();
56 780 op->do_cancel();
57 780 }
58 };
59
60 /** Function pointer type for cancellation hook. */
61 using cancel_func_type = void (*)(overlapped_op*) noexcept;
62
63 long ready_ = 0;
64 std::coroutine_handle<> h;
65 capy::executor_ref ex;
66 std::error_code* ec_out = nullptr;
67 std::size_t* bytes_out = nullptr;
68 DWORD dwError = 0;
69 DWORD bytes_transferred = 0;
70 bool empty_buffer = false;
71 bool is_read_ = false;
72 std::atomic<bool> cancelled{false};
73 std::optional<std::stop_callback<canceller>> stop_cb;
74 cancel_func_type cancel_func_ = nullptr;
75
76 12358 explicit overlapped_op(func_type func) noexcept : scheduler_op(func)
77 {
78 12358 reset_overlapped();
79 12358 }
80
81 485363 void reset_overlapped() noexcept
82 {
83 485363 Internal = 0;
84 485363 InternalHigh = 0;
85 485363 Offset = 0;
86 485363 OffsetHigh = 0;
87 485363 hEvent = nullptr;
88 485363 }
89
90 473005 void reset() noexcept
91 {
92 473005 reset_overlapped();
93 473005 ready_ = 0;
94 473005 dwError = 0;
95 473005 bytes_transferred = 0;
96 473005 empty_buffer = false;
97 473005 is_read_ = false;
98 473005 cancelled.store(false, std::memory_order_relaxed);
99 473005 }
100
101 7174 void request_cancel() noexcept
102 {
103 7174 cancelled.store(true, std::memory_order_release);
104 7174 }
105
106 780 void do_cancel() noexcept
107 {
108
1/2
✓ Branch 2 → 3 taken 780 times.
✗ Branch 2 → 4 not taken.
780 if (cancel_func_)
109 780 cancel_func_(this);
110 780 }
111
112 473005 void start(std::stop_token token)
113 {
114 473005 cancelled.store(false, std::memory_order_release);
115 473005 stop_cb.reset();
116
117
2/2
✓ Branch 5 → 6 taken 1527 times.
✓ Branch 5 → 8 taken 471478 times.
473005 if (token.stop_possible())
118 1527 stop_cb.emplace(token, canceller{this});
119 473005 }
120
121 472978 void store_result(DWORD bytes, DWORD err) noexcept
122 {
123 472978 bytes_transferred = bytes;
124 472978 dwError = err;
125 472978 }
126
127 /** Write results to output parameters and resume coroutine. */
128 471769 void invoke_handler()
129 {
130 471769 stop_cb.reset();
131
132
1/2
✓ Branch 3 → 4 taken 471769 times.
✗ Branch 3 → 18 not taken.
471769 if (ec_out)
133 {
134
2/2
✓ Branch 5 → 6 taken 1855 times.
✓ Branch 5 → 8 taken 469914 times.
471769 if (cancelled.load(std::memory_order_acquire))
135 1855 *ec_out = capy::error::canceled;
136
2/2
✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 10 taken 469906 times.
469914 else if (dwError != 0)
137 8 *ec_out = make_err(dwError);
138
6/6
✓ Branch 10 → 11 taken 234186 times.
✓ Branch 10 → 15 taken 235720 times.
✓ Branch 11 → 12 taken 12 times.
✓ Branch 11 → 15 taken 234174 times.
✓ Branch 12 → 13 taken 11 times.
✓ Branch 12 → 15 taken 1 time.
469906 else if (is_read_ && bytes_transferred == 0 && !empty_buffer)
139 11 *ec_out = capy::error::eof;
140 else
141 469895 *ec_out = {};
142 }
143
144
2/2
✓ Branch 18 → 19 taken 470567 times.
✓ Branch 18 → 20 taken 1202 times.
471769 if (bytes_out)
145 470567 *bytes_out = static_cast<std::size_t>(bytes_transferred);
146
147
2/2
✓ Branch 20 → 21 taken 471769 times.
✓ Branch 21 → 22 taken 471769 times.
471769 dispatch_coro(ex, h).resume();
148 471769 }
149
150 /** Disarm cancellation and abandon the coroutine handle. */
151 void cleanup_only()
152 {
153 stop_cb.reset();
154 h = {};
155 }
156 };
157
158 /** Cast OVERLAPPED* to overlapped_op*.
159
160 Safe because overlapped_op has OVERLAPPED as first base class.
161 */
162 inline overlapped_op*
163 472978 overlapped_to_op(LPOVERLAPPED ov) noexcept
164 {
165
1/2
✓ Branch 2 → 3 taken 472978 times.
✗ Branch 2 → 4 not taken.
472978 return static_cast<overlapped_op*>(ov);
166 }
167
168 } // namespace boost::corosio::detail
169
170 #endif // BOOST_COROSIO_HAS_IOCP
171
172 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_OVERLAPPED_OP_HPP
173