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

93.4% Lines (57/61) 90.9% List of functions (10/11) 86.4% Branches (19/22)
win_overlapped_op.hpp
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 755x void operator()() const noexcept
54 {
55 755x op->request_cancel();
56 755x op->do_cancel();
57 755x }
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 detail::continuation_op cont_op;
66 capy::executor_ref ex;
67 std::error_code* ec_out = nullptr;
68 std::size_t* bytes_out = nullptr;
69 DWORD dwError = 0;
70 DWORD bytes_transferred = 0;
71 bool empty_buffer = false;
72 bool is_read_ = false;
73 std::atomic<bool> cancelled{false};
74 std::optional<std::stop_callback<canceller>> stop_cb;
75 cancel_func_type cancel_func_ = nullptr;
76
77 12510x explicit overlapped_op(func_type func) noexcept : scheduler_op(func)
78 {
79 12510x reset_overlapped();
80 12510x }
81
82 483475x void reset_overlapped() noexcept
83 {
84 483475x Internal = 0;
85 483475x InternalHigh = 0;
86 483475x Offset = 0;
87 483475x OffsetHigh = 0;
88 483475x hEvent = nullptr;
89 483475x }
90
91 470965x void reset() noexcept
92 {
93 470965x reset_overlapped();
94 470965x ready_ = 0;
95 470965x dwError = 0;
96 470965x bytes_transferred = 0;
97 470965x empty_buffer = false;
98 470965x is_read_ = false;
99 470965x cancelled.store(false, std::memory_order_relaxed);
100 470965x }
101
102 7167x void request_cancel() noexcept
103 {
104 7167x cancelled.store(true, std::memory_order_release);
105 7167x }
106
107 755x void do_cancel() noexcept
108 {
109
1/2
✓ Branch 2 → 3 taken 755 times.
✗ Branch 2 → 4 not taken.
755x if (cancel_func_)
110 755x cancel_func_(this);
111 755x }
112
113 470965x void start(std::stop_token token)
114 {
115 470965x cancelled.store(false, std::memory_order_release);
116 470965x stop_cb.reset();
117
118
2/2
✓ Branch 5 → 6 taken 1475 times.
✓ Branch 5 → 8 taken 469490 times.
470965x if (token.stop_possible())
119 1475x stop_cb.emplace(token, canceller{this});
120 470965x }
121
122 470938x void store_result(DWORD bytes, DWORD err) noexcept
123 {
124 470938x bytes_transferred = bytes;
125 470938x dwError = err;
126 470938x }
127
128 /** Write results to output parameters and resume coroutine. */
129 469636x void invoke_handler()
130 {
131 469636x stop_cb.reset();
132
133
1/2
✓ Branch 3 → 4 taken 469636 times.
✗ Branch 3 → 18 not taken.
469636x if (ec_out)
134 {
135
2/2
✓ Branch 5 → 6 taken 1832 times.
✓ Branch 5 → 8 taken 467804 times.
469636x if (cancelled.load(std::memory_order_acquire))
136 1832x *ec_out = capy::error::canceled;
137
2/2
✓ Branch 8 → 9 taken 13 times.
✓ Branch 8 → 10 taken 467791 times.
467804x else if (dwError != 0)
138 13x *ec_out = make_err(dwError);
139
6/6
✓ Branch 10 → 11 taken 233135 times.
✓ Branch 10 → 15 taken 234656 times.
✓ Branch 11 → 12 taken 12 times.
✓ Branch 11 → 15 taken 233123 times.
✓ Branch 12 → 13 taken 11 times.
✓ Branch 12 → 15 taken 1 time.
467791x else if (is_read_ && bytes_transferred == 0 && !empty_buffer)
140 11x *ec_out = capy::error::eof;
141 else
142 467780x *ec_out = {};
143 }
144
145
2/2
✓ Branch 18 → 19 taken 468460 times.
✓ Branch 18 → 20 taken 1176 times.
469636x if (bytes_out)
146 468460x *bytes_out = static_cast<std::size_t>(bytes_transferred);
147
148 469636x cont_op.cont.h = h;
149
2/2
✓ Branch 20 → 21 taken 469636 times.
✓ Branch 21 → 22 taken 469636 times.
469636x dispatch_coro(ex, cont_op.cont).resume();
150 469636x }
151
152 /** Disarm cancellation and abandon the coroutine handle. */
153 void cleanup_only()
154 {
155 stop_cb.reset();
156 h = {};
157 }
158 };
159
160 /** Cast OVERLAPPED* to overlapped_op*.
161
162 Safe because overlapped_op has OVERLAPPED as first base class.
163 */
164 inline overlapped_op*
165 470942x overlapped_to_op(LPOVERLAPPED ov) noexcept
166 {
167
1/2
✓ Branch 2 → 3 taken 470942 times.
✗ Branch 2 → 4 not taken.
470942x return static_cast<overlapped_op*>(ov);
168 }
169
170 } // namespace boost::corosio::detail
171
172 #endif // BOOST_COROSIO_HAS_IOCP
173
174 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_OVERLAPPED_OP_HPP
175