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

93.3% Lines (56/60) 90.9% List of functions (10/11) 86.4% Branches (19/22)
f(x) Functions (11)
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/native/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 809x void operator()() const noexcept
54 {
55 809x op->request_cancel();
56 809x op->do_cancel();
57 809x }
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 12804x explicit overlapped_op(func_type func) noexcept : scheduler_op(func)
77 {
78 12804x reset_overlapped();
79 12804x }
80
81 558249x void reset_overlapped() noexcept
82 {
83 558249x Internal = 0;
84 558249x InternalHigh = 0;
85 558249x Offset = 0;
86 558249x OffsetHigh = 0;
87 558249x hEvent = nullptr;
88 558249x }
89
90 545445x void reset() noexcept
91 {
92 545445x reset_overlapped();
93 545445x ready_ = 0;
94 545445x dwError = 0;
95 545445x bytes_transferred = 0;
96 545445x empty_buffer = false;
97 545445x is_read_ = false;
98 545445x cancelled.store(false, std::memory_order_relaxed);
99 545445x }
100
101 7318x void request_cancel() noexcept
102 {
103 7318x cancelled.store(true, std::memory_order_release);
104 7318x }
105
106 809x void do_cancel() noexcept
107 {
108
1/2
✓ Branch 2 → 3 taken 809 times.
✗ Branch 2 → 4 not taken.
809x if (cancel_func_)
109 809x cancel_func_(this);
110 809x }
111
112 545445x void start(std::stop_token token)
113 {
114 545445x cancelled.store(false, std::memory_order_release);
115 545445x stop_cb.reset();
116
117
2/2
✓ Branch 5 → 6 taken 1586 times.
✓ Branch 5 → 8 taken 543859 times.
545445x if (token.stop_possible())
118 1586x stop_cb.emplace(token, canceller{this});
119 545445x }
120
121 545418x void store_result(DWORD bytes, DWORD err) noexcept
122 {
123 545418x bytes_transferred = bytes;
124 545418x dwError = err;
125 545418x }
126
127 /** Write results to output parameters and resume coroutine. */
128 544191x void invoke_handler()
129 {
130 544191x stop_cb.reset();
131
132
1/2
✓ Branch 3 → 4 taken 544191 times.
✗ Branch 3 → 18 not taken.
544191x if (ec_out)
133 {
134
2/2
✓ Branch 5 → 6 taken 1904 times.
✓ Branch 5 → 8 taken 542287 times.
544191x if (cancelled.load(std::memory_order_acquire))
135 1904x *ec_out = capy::error::canceled;
136
2/2
✓ Branch 8 → 9 taken 12 times.
✓ Branch 8 → 10 taken 542275 times.
542287x else if (dwError != 0)
137 12x *ec_out = make_err(dwError);
138
6/6
✓ Branch 10 → 11 taken 270352 times.
✓ Branch 10 → 15 taken 271923 times.
✓ Branch 11 → 12 taken 12 times.
✓ Branch 11 → 15 taken 270340 times.
✓ Branch 12 → 13 taken 11 times.
✓ Branch 12 → 15 taken 1 time.
542275x else if (is_read_ && bytes_transferred == 0 && !empty_buffer)
139 11x *ec_out = capy::error::eof;
140 else
141 542264x *ec_out = {};
142 }
143
144
2/2
✓ Branch 18 → 19 taken 542964 times.
✓ Branch 18 → 20 taken 1227 times.
544191x if (bytes_out)
145 542964x *bytes_out = static_cast<std::size_t>(bytes_transferred);
146
147
2/2
✓ Branch 20 → 21 taken 544191 times.
✓ Branch 21 → 22 taken 544191 times.
544191x dispatch_coro(ex, h).resume();
148 544191x }
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 545422x overlapped_to_op(LPOVERLAPPED ov) noexcept
164 {
165
1/2
✓ Branch 2 → 3 taken 545422 times.
✗ Branch 2 → 4 not taken.
545422x 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