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

97.2% Lines (307/316) 97.8% List of functions (44/45) 85.2% Branches (127/149)
win_random_access_file_service.hpp
f(x) Functions (45)
Function Calls Lines Branches Blocks
boost::corosio::detail::raf_concurrent_op::raf_concurrent_op(boost::corosio::detail::win_random_access_file_internal&) :127 165x 100.0% 100.0% boost::corosio::detail::raf_concurrent_op::do_cancel_impl(boost::corosio::detail::overlapped_op*) :136 5x 100.0% 50.0% 88.9% boost::corosio::detail::raf_concurrent_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :145 165x 72.4% 71.4% 71.1% boost::corosio::detail::win_random_access_file_internal::win_random_access_file_internal(boost::corosio::detail::win_random_access_file_service&) :204 55x 100.0% 100.0% boost::corosio::detail::win_random_access_file_internal::~win_random_access_file_internal() :210 55x 100.0% 100.0% boost::corosio::detail::win_random_access_file_internal::native_handle() const :216 356x 100.0% 100.0% boost::corosio::detail::win_random_access_file_internal::is_open() const :222 5x 100.0% 100.0% boost::corosio::detail::win_random_access_file_internal::cancel() :228 2x 100.0% 50.0% 100.0% boost::corosio::detail::win_random_access_file_internal::cancel()::{lambda(boost::corosio::detail::raf_concurrent_op*)#1}::operator()(boost::corosio::detail::raf_concurrent_op*) const :235 0 0.0% 0.0% boost::corosio::detail::win_random_access_file_internal::close_handle() :239 156x 100.0% 100.0% 100.0% boost::corosio::detail::win_random_access_file_internal::size() const :250 6x 100.0% 100.0% 77.8% boost::corosio::detail::win_random_access_file_internal::resize(unsigned long long) :260 5x 100.0% 100.0% 90.9% boost::corosio::detail::win_random_access_file_internal::sync_data() :272 3x 100.0% 100.0% 88.9% boost::corosio::detail::win_random_access_file_internal::sync_all() :283 2x 100.0% 100.0% 83.3% boost::corosio::detail::win_random_access_file_internal::release() :291 2x 100.0% 100.0% boost::corosio::detail::win_random_access_file_internal::assign(unsigned long long) :299 4x 100.0% 100.0% 87.5% boost::corosio::detail::win_random_access_file_internal::read_some_at(unsigned long long, std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :314 145x 100.0% 81.0% 93.1% boost::corosio::detail::win_random_access_file_internal::write_some_at(unsigned long long, std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :395 20x 100.0% 90.5% 94.8% boost::corosio::detail::win_random_access_file::win_random_access_file(std::shared_ptr<boost::corosio::detail::win_random_access_file_internal>) :478 55x 100.0% 100.0% boost::corosio::detail::win_random_access_file::close_internal() :485 54x 100.0% 50.0% 100.0% boost::corosio::detail::win_random_access_file::read_some_at(unsigned long long, std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :495 145x 100.0% 100.0% 80.0% boost::corosio::detail::win_random_access_file::write_some_at(unsigned long long, std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :508 20x 100.0% 100.0% 80.0% boost::corosio::detail::win_random_access_file::native_handle() const :521 351x 100.0% 100.0% boost::corosio::detail::win_random_access_file::cancel() :527 2x 100.0% 100.0% boost::corosio::detail::win_random_access_file::size() const :533 6x 100.0% 100.0% boost::corosio::detail::win_random_access_file::resize(unsigned long long) :539 5x 100.0% 100.0% boost::corosio::detail::win_random_access_file::sync_data() :545 3x 100.0% 100.0% boost::corosio::detail::win_random_access_file::sync_all() :551 2x 100.0% 100.0% boost::corosio::detail::win_random_access_file::release() :557 2x 100.0% 100.0% boost::corosio::detail::win_random_access_file::assign(unsigned long long) :563 4x 100.0% 100.0% boost::corosio::detail::win_random_access_file::get_internal() const :569 140x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::win_random_access_file_service(boost::capy::execution_context&) :578 1247x 100.0% 83.3% 80.0% boost::corosio::detail::win_random_access_file_service::~win_random_access_file_service() :592 2494x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::construct() :600 55x 100.0% 100.0% 89.5% boost::corosio::detail::win_random_access_file_service::destroy(boost::corosio::io_object::implementation*) :620 54x 100.0% 50.0% 100.0% boost::corosio::detail::win_random_access_file_service::close(boost::corosio::io_object::handle&) :631 97x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::shutdown() :638 1247x 100.0% 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::open_file(boost::corosio::random_access_file::implementation&, std::filesystem::__cxx11::path const&, boost::corosio::file_base::flags) :650 64x 100.0% 97.2% 87.5% boost::corosio::detail::win_random_access_file_service::destroy_impl(boost::corosio::detail::win_random_access_file&) :716 54x 100.0% 50.0% 100.0% boost::corosio::detail::win_random_access_file_service::unregister_impl(boost::corosio::detail::win_random_access_file_internal&) :726 55x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::on_pending(boost::corosio::detail::overlapped_op*) :740 156x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::on_completion(boost::corosio::detail::overlapped_op*, unsigned long, unsigned long) :746 9x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::work_started() :753 165x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::iocp_handle() const :765 4x 100.0% 100.0% boost::corosio::detail::win_random_access_file_service::try_flush_data(void*) :771 3x 100.0% 75.0% 85.7%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
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_RANDOM_ACCESS_FILE_SERVICE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_RANDOM_ACCESS_FILE_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/except.hpp>
19 #include <boost/corosio/detail/random_access_file_service.hpp>
20 #include <boost/capy/ex/execution_context.hpp>
21 #include <boost/corosio/detail/intrusive.hpp>
22 #include <boost/corosio/native/detail/iocp/win_mutex.hpp>
23 #include <boost/corosio/native/detail/iocp/win_random_access_file.hpp>
24 #include <boost/corosio/native/detail/iocp/win_scheduler.hpp>
25 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
26 #include <boost/corosio/native/detail/make_err.hpp>
27 #include <boost/corosio/detail/buffer_param.hpp>
28 #include <boost/capy/buffers.hpp>
29 #include <boost/capy/error.hpp>
30
31 #include <filesystem>
32
33 namespace boost::corosio::detail {
34
35 /** Windows IOCP random-access file management service.
36
37 Owns all random-access file implementations and coordinates
38 their lifecycle with the IOCP.
39
40 @par Thread Safety
41 All public member functions are thread-safe.
42 */
43 class BOOST_COROSIO_DECL win_random_access_file_service final
44 : public random_access_file_service
45 {
46 public:
47 using key_type = win_random_access_file_service;
48
49 explicit win_random_access_file_service(capy::execution_context& ctx);
50 ~win_random_access_file_service();
51
52 win_random_access_file_service(win_random_access_file_service const&) =
53 delete;
54 win_random_access_file_service&
55 operator=(win_random_access_file_service const&) = delete;
56
57 io_object::implementation* construct() override;
58 void destroy(io_object::implementation* p) override;
59 void close(io_object::handle& h) override;
60 void shutdown() override;
61
62 std::error_code open_file(
63 random_access_file::implementation& impl,
64 std::filesystem::path const& path,
65 file_base::flags mode) override;
66
67 void destroy_impl(win_random_access_file& impl);
68 void unregister_impl(win_random_access_file_internal& impl);
69
70 void post(overlapped_op* op);
71 void on_pending(overlapped_op* op) noexcept;
72 void on_completion(overlapped_op* op, DWORD error, DWORD bytes) noexcept;
73 void work_started() noexcept;
74 void work_finished() noexcept;
75
76 void* iocp_handle() const noexcept;
77
78 /** Attempt data-only flush via NtFlushBuffersFileEx.
79
80 @return true if data-only flush succeeded, false if
81 caller should fall back to FlushFileBuffers.
82 */
83 bool try_flush_data(HANDLE h) noexcept;
84
85 private:
86 // NtFlushBuffersFileEx support for data-only sync
87 struct io_status_block
88 {
89 union
90 {
91 LONG Status;
92 void* Pointer;
93 };
94 ULONG_PTR Information;
95 };
96
97 enum
98 {
99 flush_flags_file_data_sync_only = 4
100 };
101
102 using nt_flush_fn =
103 LONG(NTAPI*)(HANDLE, ULONG, void*, ULONG, io_status_block*);
104
105 win_scheduler& sched_;
106 BOOST_COROSIO_MSVC_WARNING_PUSH
107 BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) // detail:: members, dll-interface
108 win_mutex mutex_;
109 intrusive_list<win_random_access_file_internal> file_list_;
110 intrusive_list<win_random_access_file> wrapper_list_;
111 BOOST_COROSIO_MSVC_WARNING_POP
112 void* iocp_;
113 nt_flush_fn nt_flush_buffers_file_ex_;
114 };
115
116 /** Get or create the random-access file service for the given context. */
117 inline win_random_access_file_service&
118 get_random_access_file_service(capy::execution_context& ctx, win_scheduler&)
119 {
120 return ctx.make_service<win_random_access_file_service>();
121 }
122
123 // ---------------------------------------------------------------------------
124 // raf_concurrent_op
125 // ---------------------------------------------------------------------------
126
127 165x inline raf_concurrent_op::raf_concurrent_op(
128 165x win_random_access_file_internal& f) noexcept
129 : overlapped_op(&do_complete)
130 165x , file_(&f)
131 {
132 165x cancel_func_ = &do_cancel_impl;
133 165x }
134
135 inline void
136 5x raf_concurrent_op::do_cancel_impl(overlapped_op* base) noexcept
137 {
138 5x auto* op = static_cast<raf_concurrent_op*>(base);
139 5x op->cancelled.store(true, std::memory_order_release);
140
1/2
✓ Branch 4 → 5 taken 5 times.
✗ Branch 4 → 10 not taken.
5x if (op->file_->is_open())
141
1/2
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 7 not taken.
5x ::CancelIoEx(op->file_->native_handle(), op);
142 5x }
143
144 inline void
145 165x raf_concurrent_op::do_complete(
146 void* owner,
147 scheduler_op* base,
148 std::uint32_t /*bytes*/,
149 std::uint32_t /*error*/)
150 {
151 165x auto* op = static_cast<raf_concurrent_op*>(base);
152
153
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 12 taken 165 times.
165x if (!owner)
154 {
155 // Shutdown path: clean up without invoking
156 op->stop_cb.reset();
157 op->h = {};
158 {
159 std::lock_guard<win_mutex> lock(op->file_->ops_mutex_);
160 op->file_->outstanding_ops_.remove(op);
161 }
162 op->file_ref.reset();
163 delete op;
164 return;
165 }
166
167 // Normal completion
168 165x op->stop_cb.reset();
169
170
1/2
✓ Branch 13 → 14 taken 165 times.
✗ Branch 13 → 28 not taken.
165x if (op->ec_out)
171 {
172
2/2
✓ Branch 15 → 16 taken 5 times.
✓ Branch 15 → 18 taken 160 times.
165x if (op->cancelled.load(std::memory_order_acquire))
173 5x *op->ec_out = capy::error::canceled;
174
2/2
✓ Branch 18 → 19 taken 10 times.
✓ Branch 18 → 20 taken 150 times.
160x else if (op->dwError != 0)
175 // Through the IOCP normalization point, like every other
176 // overlapped op — plain make_err would leave codes such
177 // as ERROR_INVALID_HANDLE to toolchain-dependent mappings.
178 10x *op->ec_out = iocp_make_err(op->dwError, /*accept_path=*/false);
179
6/6
✓ Branch 20 → 21 taken 135 times.
✓ Branch 20 → 25 taken 15 times.
✓ Branch 21 → 22 taken 2 times.
✓ Branch 21 → 25 taken 133 times.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 25 taken 1 time.
150x else if (op->is_read && op->bytes_transferred == 0 && !op->empty_buffer)
180 1x *op->ec_out = capy::error::eof;
181 else
182 149x *op->ec_out = {};
183 }
184
185
1/2
✓ Branch 28 → 29 taken 165 times.
✗ Branch 28 → 30 not taken.
165x if (op->bytes_out)
186 165x *op->bytes_out = static_cast<std::size_t>(op->bytes_transferred);
187
188 {
189 165x std::lock_guard<win_mutex> lock(op->file_->ops_mutex_);
190 165x op->file_->outstanding_ops_.remove(op);
191 165x }
192
193 165x op->file_ref.reset();
194
195 165x auto coro = op->h;
196
1/2
✓ Branch 34 → 35 taken 165 times.
✗ Branch 34 → 36 not taken.
165x delete op;
197
1/1
✓ Branch 36 → 37 taken 165 times.
165x coro.resume();
198 }
199
200 // ---------------------------------------------------------------------------
201 // win_random_access_file_internal
202 // ---------------------------------------------------------------------------
203
204 55x inline win_random_access_file_internal::win_random_access_file_internal(
205 55x win_random_access_file_service& svc) noexcept
206 55x : svc_(svc)
207 {
208 55x }
209
210 55x inline win_random_access_file_internal::~win_random_access_file_internal()
211 {
212 55x svc_.unregister_impl(*this);
213 55x }
214
215 inline HANDLE
216 356x win_random_access_file_internal::native_handle() const noexcept
217 {
218 356x return handle_;
219 }
220
221 inline bool
222 5x win_random_access_file_internal::is_open() const noexcept
223 {
224 5x return handle_ != INVALID_HANDLE_VALUE;
225 }
226
227 inline void
228 2x win_random_access_file_internal::cancel() noexcept
229 {
230
1/2
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 4 not taken.
2x if (handle_ != INVALID_HANDLE_VALUE)
231 2x ::CancelIoEx(handle_, nullptr);
232
233 2x std::lock_guard<win_mutex> lock(ops_mutex_);
234 2x outstanding_ops_.for_each(
235 [](raf_concurrent_op* op) { op->request_cancel(); });
236 2x }
237
238 inline void
239 156x win_random_access_file_internal::close_handle() noexcept
240 {
241
2/2
✓ Branch 2 → 3 taken 44 times.
✓ Branch 2 → 6 taken 112 times.
156x if (handle_ != INVALID_HANDLE_VALUE)
242 {
243 44x ::CancelIoEx(handle_, nullptr);
244 44x ::CloseHandle(handle_);
245 44x handle_ = INVALID_HANDLE_VALUE;
246 }
247 156x }
248
249 inline std::uint64_t
250 6x win_random_access_file_internal::size() const
251 {
252 LARGE_INTEGER li;
253
3/3
✓ Branch 2 → 3 taken 6 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 5 times.
6x if (!::GetFileSizeEx(handle_, &li))
254 1x throw_system_error(
255
1/1
✓ Branch 4 → 5 taken 1 time.
2x make_err(::GetLastError()), "random_access_file::size");
256 5x return static_cast<std::uint64_t>(li.QuadPart);
257 }
258
259 inline std::error_code
260 5x win_random_access_file_internal::resize(std::uint64_t new_size) noexcept
261 {
262 LARGE_INTEGER li;
263 5x li.QuadPart = static_cast<LONGLONG>(new_size);
264
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 4 times.
5x if (!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))
265 1x return make_err(::GetLastError());
266
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 10 taken 2 times.
4x if (!::SetEndOfFile(handle_))
267 2x return make_err(::GetLastError());
268 2x return {};
269 }
270
271 inline std::error_code
272 3x win_random_access_file_internal::sync_data() noexcept
273 {
274 // Attempt data-only flush; fall back to full flush
275
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 2 times.
3x if (svc_.try_flush_data(handle_))
276 1x return {};
277
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 9 taken 1 time.
2x if (!::FlushFileBuffers(handle_))
278 1x return make_err(::GetLastError());
279 1x return {};
280 }
281
282 inline std::error_code
283 2x win_random_access_file_internal::sync_all() noexcept
284 {
285
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 1 time.
2x if (!::FlushFileBuffers(handle_))
286 1x return make_err(::GetLastError());
287 1x return {};
288 }
289
290 inline native_handle_type
291 2x win_random_access_file_internal::release()
292 {
293 2x HANDLE h = handle_;
294 2x handle_ = INVALID_HANDLE_VALUE;
295 2x return reinterpret_cast<native_handle_type>(h);
296 }
297
298 inline std::error_code
299 4x win_random_access_file_internal::assign(native_handle_type handle) noexcept
300 {
301 4x close_handle();
302 4x HANDLE h = reinterpret_cast<HANDLE>(handle);
303 // Register with IOCP so overlapped I/O works
304
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 8 taken 3 times.
4x if (!::CreateIoCompletionPort(
305 4x h, static_cast<HANDLE>(svc_.iocp_handle()), key_io, 0))
306 {
307 1x return make_err(::GetLastError());
308 }
309 3x handle_ = h;
310 3x return {};
311 }
312
313 inline std::coroutine_handle<>
314 145x win_random_access_file_internal::read_some_at(
315 std::uint64_t offset,
316 std::coroutine_handle<> h,
317 capy::executor_ref ex,
318 buffer_param param,
319 std::stop_token token,
320 std::error_code* ec,
321 std::size_t* bytes_out)
322 {
323 static constexpr std::size_t max_buffers = 16;
324
325
1/1
✓ Branch 2 → 3 taken 145 times.
145x auto* op = new raf_concurrent_op(*this);
326
1/1
✓ Branch 4 → 5 taken 145 times.
145x op->file_ref = shared_from_this();
327
328 145x op->reset();
329 145x op->is_read = true;
330 145x op->h = h;
331 145x op->ex = ex;
332 145x op->ec_out = ec;
333 145x op->bytes_out = bytes_out;
334 145x op->start(token);
335
336 145x svc_.work_started();
337
338 // Closed-object contract: complete with bad_file_descriptor
339 // without touching the kernel.
340
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 18 taken 143 times.
145x if (handle_ == INVALID_HANDLE_VALUE)
341 {
342 {
343 2x std::lock_guard<win_mutex> lock(ops_mutex_);
344 2x outstanding_ops_.push_back(op);
345 2x }
346 2x svc_.on_completion(op, ERROR_INVALID_HANDLE, 0);
347 2x return std::noop_coroutine();
348 }
349
350 143x capy::mutable_buffer bufs[max_buffers];
351 143x auto count = param.copy_to(bufs, max_buffers);
352
353
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 27 taken 142 times.
143x if (count == 0)
354 {
355 1x op->empty_buffer = true;
356 {
357 1x std::lock_guard<win_mutex> lock(ops_mutex_);
358 1x outstanding_ops_.push_back(op);
359 1x }
360 1x svc_.on_completion(op, 0, 0);
361 1x return std::noop_coroutine();
362 }
363
364 142x op->buf = bufs[0].data();
365 142x op->buf_len = static_cast<DWORD>(bufs[0].size());
366
367 // Set caller-provided offset in OVERLAPPED
368 142x op->Offset = static_cast<DWORD>(offset & 0xFFFFFFFF);
369 142x op->OffsetHigh = static_cast<DWORD>(offset >> 32);
370
371 {
372 142x std::lock_guard<win_mutex> lock(ops_mutex_);
373 142x outstanding_ops_.push_back(op);
374 142x }
375
376
2/3
✓ Branch 32 → 33 taken 142 times.
✗ Branch 32 → 34 not taken.
✓ Branch 35 → 36 taken 142 times.
142x BOOL ok = ::ReadFile(handle_, op->buf, op->buf_len, nullptr, op);
377
2/3
✓ Branch 36 → 37 taken 142 times.
✗ Branch 36 → 39 not taken.
✓ Branch 37 → 38 taken 142 times.
142x DWORD err = ok ? 0 : ::GetLastError();
378
379
3/4
✓ Branch 40 → 41 taken 142 times.
✗ Branch 40 → 46 not taken.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 46 taken 140 times.
142x if (err != 0 && err != ERROR_IO_PENDING)
380 {
381 2x svc_.on_completion(op, err, 0);
382 2x return std::noop_coroutine();
383 }
384
385 140x svc_.on_pending(op);
386
387 // Re-check cancellation after I/O is pending
388
2/2
✓ Branch 48 → 49 taken 3 times.
✓ Branch 48 → 53 taken 137 times.
140x if (op->cancelled.load(std::memory_order_acquire))
389
2/3
✓ Branch 49 → 50 taken 3 times.
✗ Branch 49 → 51 not taken.
✓ Branch 52 → 53 taken 3 times.
3x ::CancelIoEx(handle_, op);
390
391 140x return std::noop_coroutine();
392 }
393
394 inline std::coroutine_handle<>
395 20x win_random_access_file_internal::write_some_at(
396 std::uint64_t offset,
397 std::coroutine_handle<> h,
398 capy::executor_ref ex,
399 buffer_param param,
400 std::stop_token token,
401 std::error_code* ec,
402 std::size_t* bytes_out)
403 {
404 static constexpr std::size_t max_buffers = 16;
405
406
1/1
✓ Branch 2 → 3 taken 20 times.
20x auto* op = new raf_concurrent_op(*this);
407
1/1
✓ Branch 4 → 5 taken 20 times.
20x op->file_ref = shared_from_this();
408
409 20x op->reset();
410 20x op->is_read = false;
411 20x op->h = h;
412 20x op->ex = ex;
413 20x op->ec_out = ec;
414 20x op->bytes_out = bytes_out;
415 20x op->start(token);
416
417 20x svc_.work_started();
418
419 // Closed-object contract: complete with bad_file_descriptor
420 // without touching the kernel.
421
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 18 taken 19 times.
20x if (handle_ == INVALID_HANDLE_VALUE)
422 {
423 {
424 1x std::lock_guard<win_mutex> lock(ops_mutex_);
425 1x outstanding_ops_.push_back(op);
426 1x }
427 1x svc_.on_completion(op, ERROR_INVALID_HANDLE, 0);
428 1x return std::noop_coroutine();
429 }
430
431 19x capy::mutable_buffer bufs[max_buffers];
432 19x auto count = param.copy_to(bufs, max_buffers);
433
434
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 27 taken 18 times.
19x if (count == 0)
435 {
436 {
437 1x std::lock_guard<win_mutex> lock(ops_mutex_);
438 1x outstanding_ops_.push_back(op);
439 1x }
440 1x svc_.on_completion(op, 0, 0);
441 1x return std::noop_coroutine();
442 }
443
444 18x op->buf = bufs[0].data();
445 18x op->buf_len = static_cast<DWORD>(bufs[0].size());
446
447 // Set caller-provided offset in OVERLAPPED
448 18x op->Offset = static_cast<DWORD>(offset & 0xFFFFFFFF);
449 18x op->OffsetHigh = static_cast<DWORD>(offset >> 32);
450
451 {
452 18x std::lock_guard<win_mutex> lock(ops_mutex_);
453 18x outstanding_ops_.push_back(op);
454 18x }
455
456
2/3
✓ Branch 32 → 33 taken 18 times.
✗ Branch 32 → 34 not taken.
✓ Branch 35 → 36 taken 18 times.
18x BOOL ok = ::WriteFile(handle_, op->buf, op->buf_len, nullptr, op);
457
3/3
✓ Branch 36 → 37 taken 5 times.
✓ Branch 36 → 39 taken 13 times.
✓ Branch 37 → 38 taken 5 times.
18x DWORD err = ok ? 0 : ::GetLastError();
458
459
4/4
✓ Branch 40 → 41 taken 5 times.
✓ Branch 40 → 46 taken 13 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 46 taken 3 times.
18x if (err != 0 && err != ERROR_IO_PENDING)
460 {
461 2x svc_.on_completion(op, err, 0);
462 2x return std::noop_coroutine();
463 }
464
465 16x svc_.on_pending(op);
466
467 // Re-check cancellation after I/O is pending
468
2/2
✓ Branch 48 → 49 taken 2 times.
✓ Branch 48 → 53 taken 14 times.
16x if (op->cancelled.load(std::memory_order_acquire))
469
2/3
✓ Branch 49 → 50 taken 2 times.
✗ Branch 49 → 51 not taken.
✓ Branch 52 → 53 taken 2 times.
2x ::CancelIoEx(handle_, op);
470
471 16x return std::noop_coroutine();
472 }
473
474 // ---------------------------------------------------------------------------
475 // win_random_access_file wrapper
476 // ---------------------------------------------------------------------------
477
478 55x inline win_random_access_file::win_random_access_file(
479 55x std::shared_ptr<win_random_access_file_internal> internal) noexcept
480 55x : internal_(std::move(internal))
481 {
482 55x }
483
484 inline void
485 54x win_random_access_file::close_internal() noexcept
486 {
487
1/2
✓ Branch 3 → 4 taken 54 times.
✗ Branch 3 → 7 not taken.
54x if (internal_)
488 {
489 54x internal_->close_handle();
490 54x internal_.reset();
491 }
492 54x }
493
494 inline std::coroutine_handle<>
495 145x win_random_access_file::read_some_at(
496 std::uint64_t offset,
497 std::coroutine_handle<> h,
498 capy::executor_ref d,
499 buffer_param buf,
500 std::stop_token token,
501 std::error_code* ec,
502 std::size_t* bytes)
503 {
504
1/1
✓ Branch 4 → 5 taken 145 times.
145x return internal_->read_some_at(offset, h, d, buf, token, ec, bytes);
505 }
506
507 inline std::coroutine_handle<>
508 20x win_random_access_file::write_some_at(
509 std::uint64_t offset,
510 std::coroutine_handle<> h,
511 capy::executor_ref d,
512 buffer_param buf,
513 std::stop_token token,
514 std::error_code* ec,
515 std::size_t* bytes)
516 {
517
1/1
✓ Branch 4 → 5 taken 20 times.
20x return internal_->write_some_at(offset, h, d, buf, token, ec, bytes);
518 }
519
520 inline native_handle_type
521 351x win_random_access_file::native_handle() const noexcept
522 {
523 351x return reinterpret_cast<native_handle_type>(internal_->native_handle());
524 }
525
526 inline void
527 2x win_random_access_file::cancel() noexcept
528 {
529 2x internal_->cancel();
530 2x }
531
532 inline std::uint64_t
533 6x win_random_access_file::size() const
534 {
535 6x return internal_->size();
536 }
537
538 inline std::error_code
539 5x win_random_access_file::resize(std::uint64_t new_size) noexcept
540 {
541 5x return internal_->resize(new_size);
542 }
543
544 inline std::error_code
545 3x win_random_access_file::sync_data() noexcept
546 {
547 3x return internal_->sync_data();
548 }
549
550 inline std::error_code
551 2x win_random_access_file::sync_all() noexcept
552 {
553 2x return internal_->sync_all();
554 }
555
556 inline native_handle_type
557 2x win_random_access_file::release()
558 {
559 2x return internal_->release();
560 }
561
562 inline std::error_code
563 4x win_random_access_file::assign(native_handle_type handle) noexcept
564 {
565 4x return internal_->assign(handle);
566 }
567
568 inline win_random_access_file_internal*
569 140x win_random_access_file::get_internal() const noexcept
570 {
571 140x return internal_.get();
572 }
573
574 // ---------------------------------------------------------------------------
575 // win_random_access_file_service
576 // ---------------------------------------------------------------------------
577
578 1247x inline win_random_access_file_service::win_random_access_file_service(
579 1247x capy::execution_context& ctx)
580 2494x : sched_(ctx.use_service<win_scheduler>())
581 1247x , iocp_(sched_.native_handle())
582
2/2
✓ Branch 3 → 4 taken 1247 times.
✓ Branch 4 → 5 taken 1247 times.
1247x , nt_flush_buffers_file_ex_(nullptr)
583 {
584
3/4
✓ Branch 8 → 9 taken 1247 times.
✓ Branch 9 → 10 taken 1247 times.
✓ Branch 10 → 11 taken 1247 times.
✗ Branch 10 → 12 not taken.
1247x if (FARPROC p = ::GetProcAddress(
585 ::GetModuleHandleA("NTDLL"), "NtFlushBuffersFileEx"))
586 {
587 1247x nt_flush_buffers_file_ex_ =
588 reinterpret_cast<nt_flush_fn>(reinterpret_cast<void*>(p));
589 }
590 1247x }
591
592 2494x inline win_random_access_file_service::~win_random_access_file_service()
593 {
594
2/2
✓ Branch 6 → 3 taken 1 time.
✓ Branch 6 → 7 taken 1247 times.
1248x for (auto* w = wrapper_list_.pop_front(); w != nullptr;
595 1x w = wrapper_list_.pop_front())
596
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1x delete w;
597 2494x }
598
599 inline io_object::implementation*
600 55x win_random_access_file_service::construct()
601 {
602
1/1
✓ Branch 2 → 3 taken 55 times.
55x auto internal = std::make_shared<win_random_access_file_internal>(*this);
603
604 {
605 55x std::lock_guard<win_mutex> lock(mutex_);
606 55x file_list_.push_back(internal.get());
607 55x }
608
609
1/1
✓ Branch 7 → 8 taken 55 times.
55x auto* wrapper = new win_random_access_file(std::move(internal));
610
611 {
612 55x std::lock_guard<win_mutex> lock(mutex_);
613 55x wrapper_list_.push_back(wrapper);
614 55x }
615
616 55x return wrapper;
617 55x }
618
619 inline void
620 54x win_random_access_file_service::destroy(io_object::implementation* p)
621 {
622
1/2
✓ Branch 2 → 3 taken 54 times.
✗ Branch 2 → 5 not taken.
54x if (p)
623 {
624 54x auto& wrapper = static_cast<win_random_access_file&>(*p);
625 54x wrapper.close_internal();
626 54x destroy_impl(wrapper);
627 }
628 54x }
629
630 inline void
631 97x win_random_access_file_service::close(io_object::handle& h)
632 {
633 97x auto& wrapper = static_cast<win_random_access_file&>(*h.get());
634 97x wrapper.get_internal()->close_handle();
635 97x }
636
637 inline void
638 1247x win_random_access_file_service::shutdown()
639 {
640 1247x std::lock_guard<win_mutex> lock(mutex_);
641
642
2/2
✓ Branch 6 → 4 taken 1 time.
✓ Branch 6 → 7 taken 1247 times.
1248x for (auto* impl = file_list_.pop_front(); impl != nullptr;
643 1x impl = file_list_.pop_front())
644 {
645 1x impl->close_handle();
646 }
647 1247x }
648
649 inline std::error_code
650 64x win_random_access_file_service::open_file(
651 random_access_file::implementation& impl,
652 std::filesystem::path const& path,
653 file_base::flags mode)
654 {
655 // Build access mask
656 64x DWORD access = 0;
657 64x unsigned a = static_cast<unsigned>(mode) & 3u;
658
2/2
✓ Branch 2 → 3 taken 29 times.
✓ Branch 2 → 4 taken 35 times.
64x if (a == 3)
659 29x access = GENERIC_READ | GENERIC_WRITE;
660
2/2
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 6 taken 27 times.
35x else if (a == 2)
661 8x access = GENERIC_WRITE;
662 else
663 27x access = GENERIC_READ;
664
665 // Build creation disposition
666 64x DWORD disposition = OPEN_EXISTING;
667
6/6
✓ Branch 8 → 9 taken 29 times.
✓ Branch 8 → 12 taken 35 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 27 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 62 times.
64x if ((mode & file_base::create) && (mode & file_base::exclusive))
668 2x disposition = CREATE_NEW;
669
6/6
✓ Branch 16 → 17 taken 27 times.
✓ Branch 16 → 20 taken 35 times.
✓ Branch 18 → 19 taken 16 times.
✓ Branch 18 → 20 taken 11 times.
✓ Branch 21 → 22 taken 16 times.
✓ Branch 21 → 23 taken 46 times.
62x else if ((mode & file_base::create) && (mode & file_base::truncate))
670 16x disposition = OPEN_ALWAYS;
671
2/2
✓ Branch 24 → 25 taken 11 times.
✓ Branch 24 → 26 taken 35 times.
46x else if (mode & file_base::create)
672 11x disposition = OPEN_ALWAYS;
673
2/2
✓ Branch 27 → 28 taken 1 time.
✓ Branch 27 → 29 taken 34 times.
35x else if (mode & file_base::truncate)
674 1x disposition = TRUNCATE_EXISTING;
675
676 // Build flags — FILE_FLAG_OVERLAPPED + FILE_FLAG_RANDOM_ACCESS
677 64x DWORD flags =
678 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_RANDOM_ACCESS;
679
2/2
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 32 taken 63 times.
64x if (mode & file_base::sync_all_on_write)
680 1x flags |= FILE_FLAG_WRITE_THROUGH;
681
682 64x HANDLE h = ::CreateFileW(
683 path.c_str(), access, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
684 disposition, flags, nullptr);
685
686
2/2
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 37 taken 61 times.
64x if (h == INVALID_HANDLE_VALUE)
687 3x return make_err(::GetLastError());
688
689 // Register with IOCP
690
2/2
✓ Branch 38 → 39 taken 9 times.
✓ Branch 38 → 42 taken 52 times.
61x if (!::CreateIoCompletionPort(h, static_cast<HANDLE>(iocp_), key_io, 0))
691 {
692 9x DWORD err = ::GetLastError();
693 9x ::CloseHandle(h);
694 9x return make_err(err);
695 }
696
697 // Handle truncation for create|truncate combo
698
7/8
✓ Branch 43 → 44 taken 18 times.
✓ Branch 43 → 48 taken 34 times.
✓ Branch 45 → 46 taken 16 times.
✓ Branch 45 → 48 taken 2 times.
✓ Branch 46 → 47 taken 16 times.
✗ Branch 46 → 48 not taken.
✓ Branch 49 → 50 taken 16 times.
✓ Branch 49 → 55 taken 36 times.
52x if ((mode & file_base::create) && (mode & file_base::truncate) &&
699 disposition == OPEN_ALWAYS)
700 {
701
2/2
✓ Branch 51 → 52 taken 9 times.
✓ Branch 51 → 55 taken 7 times.
16x if (!::SetEndOfFile(h))
702 {
703 9x DWORD err = ::GetLastError();
704 9x ::CloseHandle(h);
705 9x return make_err(err);
706 }
707 }
708
709 43x auto& internal = *static_cast<win_random_access_file&>(impl).get_internal();
710 43x internal.handle_ = h;
711
712 43x return {};
713 }
714
715 inline void
716 54x win_random_access_file_service::destroy_impl(win_random_access_file& impl)
717 {
718 {
719 54x std::lock_guard<win_mutex> lock(mutex_);
720 54x wrapper_list_.remove(&impl);
721 54x }
722
1/2
✓ Branch 5 → 6 taken 54 times.
✗ Branch 5 → 7 not taken.
54x delete &impl;
723 54x }
724
725 inline void
726 55x win_random_access_file_service::unregister_impl(
727 win_random_access_file_internal& impl)
728 {
729 55x std::lock_guard<win_mutex> lock(mutex_);
730 55x file_list_.remove(&impl);
731 55x }
732
733 inline void
734 win_random_access_file_service::post(overlapped_op* op)
735 {
736 sched_.post(op);
737 }
738
739 inline void
740 156x win_random_access_file_service::on_pending(overlapped_op* op) noexcept
741 {
742 156x sched_.on_pending(op);
743 156x }
744
745 inline void
746 9x win_random_access_file_service::on_completion(
747 overlapped_op* op, DWORD error, DWORD bytes) noexcept
748 {
749 9x sched_.on_completion(op, error, bytes);
750 9x }
751
752 inline void
753 165x win_random_access_file_service::work_started() noexcept
754 {
755 165x sched_.work_started();
756 165x }
757
758 inline void
759 win_random_access_file_service::work_finished() noexcept
760 {
761 sched_.work_finished();
762 }
763
764 inline void*
765 4x win_random_access_file_service::iocp_handle() const noexcept
766 {
767 4x return iocp_;
768 }
769
770 inline bool
771 3x win_random_access_file_service::try_flush_data(HANDLE h) noexcept
772 {
773
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 7 not taken.
3x if (nt_flush_buffers_file_ex_)
774 {
775 3x io_status_block status = {};
776 3x if (nt_flush_buffers_file_ex_(
777
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 2 times.
3x h, flush_flags_file_data_sync_only, nullptr, 0, &status) == 0)
778 1x return true;
779 }
780 2x return false;
781 }
782
783 } // namespace boost::corosio::detail
784
785 #endif // BOOST_COROSIO_HAS_IOCP
786
787 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_RANDOM_ACCESS_FILE_SERVICE_HPP
788