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

100.0% Lines (121/121) 100.0% List of functions (25/25) 92.6% Branches (63/68)
win_random_access_file_service.hpp
f(x) Functions (25)
Function Calls Lines Branches Blocks
boost::corosio::detail::win_random_access_file_internal::size() const :116 5x 100.0% 100.0% 77.8% boost::corosio::detail::win_random_access_file_internal::resize(unsigned long long) :126 5x 100.0% 100.0% 90.9% boost::corosio::detail::win_random_access_file_internal::sync_data() :138 3x 100.0% 100.0% 88.9% boost::corosio::detail::win_random_access_file_internal::sync_all() :149 2x 100.0% 100.0% 83.3% boost::corosio::detail::win_random_access_file::win_random_access_file(std::shared_ptr<boost::corosio::detail::win_random_access_file_internal>) :160 116x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::close_internal() :167 115x 100.0% 50.0% 100.0% boost::corosio::detail::win_random_access_file::read_some_at(unsigned long long, boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :177 172x 100.0% 100.0% 80.0% boost::corosio::detail::win_random_access_file::write_some_at(unsigned long long, boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::buffer_param, std::stop_token, std::error_code*, unsigned long long*) :190 44x 100.0% 100.0% 80.0% boost::corosio::detail::win_random_access_file::native_handle() const :203 598x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::cancel() :209 2x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::size() const :215 5x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::resize(unsigned long long) :221 5x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::sync_data() :227 3x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::sync_all() :233 2x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::release() :239 5x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::assign(unsigned long long) :245 8x 100.0% – 100.0% boost::corosio::detail::win_random_access_file::get_internal() const :251 310x 100.0% – 100.0% boost::corosio::detail::win_random_access_file_service::win_random_access_file_service(boost::capy::execution_context&) :260 88x 100.0% 83.3% 76.9% boost::corosio::detail::win_random_access_file_service::~win_random_access_file_service() :274 176x 100.0% – 100.0% boost::corosio::detail::win_random_access_file_service::construct() :278 116x 100.0% 100.0% 85.7% boost::corosio::detail::win_random_access_file_service::destroy(boost::corosio::io_object::implementation*) :286 115x 100.0% 50.0% 100.0% boost::corosio::detail::win_random_access_file_service::close(boost::corosio::io_object::handle&) :293 212x 100.0% – 100.0% boost::corosio::detail::win_random_access_file_service::shutdown() :301 88x 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) :307 119x 100.0% 97.2% 87.5% boost::corosio::detail::win_random_access_file_service::try_flush_data(void*) :373 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/coro_op_complete.hpp>
27 #include <boost/corosio/native/detail/make_err.hpp>
28 #include <boost/corosio/detail/buffer_param.hpp>
29 #include <boost/capy/buffers.hpp>
30 #include <boost/capy/error.hpp>
31
32 #include <filesystem>
33
34 namespace boost::corosio::detail {
35
36 /** Windows IOCP random-access file management service.
37
38 Owns all random-access file implementations and coordinates
39 their lifecycle with the IOCP.
40
41 @par Thread Safety
42 All public member functions are thread-safe.
43 */
44 class BOOST_COROSIO_DECL win_random_access_file_service final
45 : public random_access_file_service
46 {
47 public:
48 using key_type = win_random_access_file_service;
49
50 explicit win_random_access_file_service(capy::execution_context& ctx);
51 ~win_random_access_file_service();
52
53 win_random_access_file_service(win_random_access_file_service const&) =
54 delete;
55 win_random_access_file_service&
56 operator=(win_random_access_file_service const&) = delete;
57
58 io_object::implementation* construct() override;
59 void destroy(io_object::implementation* p) override;
60 void close(io_object::handle& h) override;
61 void shutdown() override;
62
63 std::error_code open_file(
64 random_access_file::implementation& impl,
65 std::filesystem::path const& path,
66 file_base::flags mode) override;
67
68 /** Attempt data-only flush via NtFlushBuffersFileEx.
69
70 @return true if data-only flush succeeded, false if
71 caller should fall back to FlushFileBuffers.
72 */
73 bool try_flush_data(HANDLE h) noexcept;
74
75 private:
76 // NtFlushBuffersFileEx support for data-only sync
77 struct io_status_block
78 {
79 union
80 {
81 LONG Status;
82 void* Pointer;
83 };
84 ULONG_PTR Information;
85 };
86
87 enum
88 {
89 flush_flags_file_data_sync_only = 4
90 };
91
92 using nt_flush_fn =
93 LONG(NTAPI*)(HANDLE, ULONG, void*, ULONG, io_status_block*);
94
95 win_scheduler& sched_;
96 BOOST_COROSIO_MSVC_WARNING_PUSH
97 BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) // detail:: members, dll-interface
98 win_handle_registry<win_random_access_file, win_handle_base> reg_;
99 BOOST_COROSIO_MSVC_WARNING_POP
100 void* iocp_;
101 nt_flush_fn nt_flush_buffers_file_ex_;
102 };
103
104 /** Get or create the random-access file service for the given context. */
105 inline win_random_access_file_service&
106 get_random_access_file_service(capy::execution_context& ctx, win_scheduler&)
107 {
108 return ctx.make_service<win_random_access_file_service>();
109 }
110
111 // ---------------------------------------------------------------------------
112 // win_random_access_file_internal
113 // ---------------------------------------------------------------------------
114
115 inline std::uint64_t
116 5x win_random_access_file_internal::size() const
117 {
118 LARGE_INTEGER li;
119
3/3
✓ Branch 2 → 3 taken 5 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 4 times.
5x if (!::GetFileSizeEx(handle_, &li))
120 1x throw_system_error(
121
1/1
✓ Branch 4 → 5 taken 1 time.
2x make_err(::GetLastError()), "random_access_file::size");
122 4x return static_cast<std::uint64_t>(li.QuadPart);
123 }
124
125 inline std::error_code
126 5x win_random_access_file_internal::resize(std::uint64_t new_size) noexcept
127 {
128 LARGE_INTEGER li;
129 5x li.QuadPart = static_cast<LONGLONG>(new_size);
130
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 4 times.
5x if (!::SetFilePointerEx(handle_, li, nullptr, FILE_BEGIN))
131 1x return make_err(::GetLastError());
132
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 10 taken 2 times.
4x if (!::SetEndOfFile(handle_))
133 2x return make_err(::GetLastError());
134 2x return {};
135 }
136
137 inline std::error_code
138 3x win_random_access_file_internal::sync_data() noexcept
139 {
140 // Attempt data-only flush; fall back to full flush
141
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 2 times.
3x if (svc_.try_flush_data(handle_))
142 1x return {};
143
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 9 taken 1 time.
2x if (!::FlushFileBuffers(handle_))
144 1x return make_err(::GetLastError());
145 1x return {};
146 }
147
148 inline std::error_code
149 2x win_random_access_file_internal::sync_all() noexcept
150 {
151
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 1 time.
2x if (!::FlushFileBuffers(handle_))
152 1x return make_err(::GetLastError());
153 1x return {};
154 }
155
156 // ---------------------------------------------------------------------------
157 // win_random_access_file wrapper
158 // ---------------------------------------------------------------------------
159
160 116x inline win_random_access_file::win_random_access_file(
161 116x std::shared_ptr<win_random_access_file_internal> internal) noexcept
162 116x : internal_(std::move(internal))
163 {
164 116x }
165
166 inline void
167 115x win_random_access_file::close_internal() noexcept
168 {
169
1/2
✓ Branch 3 → 4 taken 115 times.
✗ Branch 3 → 7 not taken.
115x if (internal_)
170 {
171 115x internal_->close_handle();
172 115x internal_.reset();
173 }
174 115x }
175
176 inline std::coroutine_handle<>
177 172x win_random_access_file::read_some_at(
178 std::uint64_t offset,
179 capy::continuation& cont,
180 capy::executor_ref d,
181 buffer_param buf,
182 std::stop_token token,
183 std::error_code* ec,
184 std::size_t* bytes)
185 {
186
1/1
✓ Branch 4 → 5 taken 172 times.
172x return internal_->read_some_at(offset, cont, d, buf, token, ec, bytes);
187 }
188
189 inline std::coroutine_handle<>
190 44x win_random_access_file::write_some_at(
191 std::uint64_t offset,
192 capy::continuation& cont,
193 capy::executor_ref d,
194 buffer_param buf,
195 std::stop_token token,
196 std::error_code* ec,
197 std::size_t* bytes)
198 {
199
1/1
✓ Branch 4 → 5 taken 44 times.
44x return internal_->write_some_at(offset, cont, d, buf, token, ec, bytes);
200 }
201
202 inline native_handle_type
203 598x win_random_access_file::native_handle() const noexcept
204 {
205 598x return reinterpret_cast<native_handle_type>(internal_->native_handle());
206 }
207
208 inline void
209 2x win_random_access_file::cancel() noexcept
210 {
211 2x internal_->cancel();
212 2x }
213
214 inline std::uint64_t
215 5x win_random_access_file::size() const
216 {
217 5x return internal_->size();
218 }
219
220 inline std::error_code
221 5x win_random_access_file::resize(std::uint64_t new_size) noexcept
222 {
223 5x return internal_->resize(new_size);
224 }
225
226 inline std::error_code
227 3x win_random_access_file::sync_data() noexcept
228 {
229 3x return internal_->sync_data();
230 }
231
232 inline std::error_code
233 2x win_random_access_file::sync_all() noexcept
234 {
235 2x return internal_->sync_all();
236 }
237
238 inline native_handle_type
239 5x win_random_access_file::release()
240 {
241 5x return internal_->release();
242 }
243
244 inline std::error_code
245 8x win_random_access_file::assign(native_handle_type handle) noexcept
246 {
247 8x return internal_->assign(handle, handle_kind::random_access_file);
248 }
249
250 inline win_random_access_file_internal*
251 310x win_random_access_file::get_internal() const noexcept
252 {
253 310x return internal_.get();
254 }
255
256 // ---------------------------------------------------------------------------
257 // win_random_access_file_service
258 // ---------------------------------------------------------------------------
259
260 88x inline win_random_access_file_service::win_random_access_file_service(
261 88x capy::execution_context& ctx)
262 176x : sched_(ctx.use_service<win_scheduler>())
263 88x , iocp_(sched_.native_handle())
264
2/2
✓ Branch 3 → 4 taken 88 times.
✓ Branch 4 → 5 taken 88 times.
88x , nt_flush_buffers_file_ex_(nullptr)
265 {
266
3/4
✓ Branch 6 → 7 taken 88 times.
✓ Branch 7 → 8 taken 88 times.
✓ Branch 8 → 9 taken 88 times.
✗ Branch 8 → 10 not taken.
88x if (FARPROC p = ::GetProcAddress(
267 ::GetModuleHandleA("NTDLL"), "NtFlushBuffersFileEx"))
268 {
269 88x nt_flush_buffers_file_ex_ =
270 reinterpret_cast<nt_flush_fn>(reinterpret_cast<void*>(p));
271 }
272 88x }
273
274 176x inline win_random_access_file_service::~win_random_access_file_service() =
275 default;
276
277 inline io_object::implementation*
278 116x win_random_access_file_service::construct()
279 {
280 auto internal = std::make_shared<win_random_access_file_internal>(
281
1/1
✓ Branch 3 → 4 taken 116 times.
116x sched_, reg_.states(), *this);
282
1/1
✓ Branch 4 → 5 taken 116 times.
232x return reg_.add(new win_random_access_file(std::move(internal)));
283 116x }
284
285 inline void
286 115x win_random_access_file_service::destroy(io_object::implementation* p)
287 {
288
1/2
✓ Branch 2 → 3 taken 115 times.
✗ Branch 2 → 4 not taken.
115x if (p)
289 115x reg_.destroy(static_cast<win_random_access_file&>(*p));
290 115x }
291
292 inline void
293 212x win_random_access_file_service::close(io_object::handle& h)
294 {
295 212x static_cast<win_random_access_file&>(*h.get())
296 212x .get_internal()
297 212x ->close_handle();
298 212x }
299
300 inline void
301 88x win_random_access_file_service::shutdown()
302 {
303 88x reg_.shutdown();
304 88x }
305
306 inline std::error_code
307 119x win_random_access_file_service::open_file(
308 random_access_file::implementation& impl,
309 std::filesystem::path const& path,
310 file_base::flags mode)
311 {
312 // Build access mask
313 119x DWORD access = 0;
314 119x unsigned a = static_cast<unsigned>(mode) & 3u;
315
2/2
✓ Branch 2 → 3 taken 30 times.
✓ Branch 2 → 4 taken 89 times.
119x if (a == 3)
316 30x access = GENERIC_READ | GENERIC_WRITE;
317
2/2
✓ Branch 4 → 5 taken 33 times.
✓ Branch 4 → 6 taken 56 times.
89x else if (a == 2)
318 33x access = GENERIC_WRITE;
319 else
320 56x access = GENERIC_READ;
321
322 // Build creation disposition
323 119x DWORD disposition = OPEN_EXISTING;
324
6/6
✓ Branch 8 → 9 taken 29 times.
✓ Branch 8 → 12 taken 90 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 27 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 117 times.
119x if ((mode & file_base::create) && (mode & file_base::exclusive))
325 2x disposition = CREATE_NEW;
326
6/6
✓ Branch 16 → 17 taken 27 times.
✓ Branch 16 → 20 taken 90 times.
✓ Branch 18 → 19 taken 16 times.
✓ Branch 18 → 20 taken 11 times.
✓ Branch 21 → 22 taken 16 times.
✓ Branch 21 → 23 taken 101 times.
117x else if ((mode & file_base::create) && (mode & file_base::truncate))
327 16x disposition = OPEN_ALWAYS;
328
2/2
✓ Branch 24 → 25 taken 11 times.
✓ Branch 24 → 26 taken 90 times.
101x else if (mode & file_base::create)
329 11x disposition = OPEN_ALWAYS;
330
2/2
✓ Branch 27 → 28 taken 1 time.
✓ Branch 27 → 29 taken 89 times.
90x else if (mode & file_base::truncate)
331 1x disposition = TRUNCATE_EXISTING;
332
333 // Build flags — FILE_FLAG_OVERLAPPED + FILE_FLAG_RANDOM_ACCESS
334 119x DWORD flags =
335 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_RANDOM_ACCESS;
336
2/2
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 32 taken 118 times.
119x if (mode & file_base::sync_all_on_write)
337 1x flags |= FILE_FLAG_WRITE_THROUGH;
338
339 119x HANDLE h = ::CreateFileW(
340 path.c_str(), access, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
341 disposition, flags, nullptr);
342
343
2/2
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 37 taken 116 times.
119x if (h == INVALID_HANDLE_VALUE)
344 3x return make_err(::GetLastError());
345
346 // Register with IOCP
347
2/2
✓ Branch 38 → 39 taken 9 times.
✓ Branch 38 → 42 taken 107 times.
116x if (!::CreateIoCompletionPort(h, static_cast<HANDLE>(iocp_), key_io, 0))
348 {
349 9x DWORD err = ::GetLastError();
350 9x ::CloseHandle(h);
351 9x return make_err(err);
352 }
353
354 // Handle truncation for create|truncate combo
355
7/8
✓ Branch 43 → 44 taken 18 times.
✓ Branch 43 → 48 taken 89 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 91 times.
107x if ((mode & file_base::create) && (mode & file_base::truncate) &&
356 disposition == OPEN_ALWAYS)
357 {
358
2/2
✓ Branch 51 → 52 taken 9 times.
✓ Branch 51 → 55 taken 7 times.
16x if (!::SetEndOfFile(h))
359 {
360 9x DWORD err = ::GetLastError();
361 9x ::CloseHandle(h);
362 9x return make_err(err);
363 }
364 }
365
366 98x auto& internal = *static_cast<win_random_access_file&>(impl).get_internal();
367 98x internal.handle_ = h;
368
369 98x return {};
370 }
371
372 inline bool
373 3x win_random_access_file_service::try_flush_data(HANDLE h) noexcept
374 {
375
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 7 not taken.
3x if (nt_flush_buffers_file_ex_)
376 {
377 3x io_status_block status = {};
378 3x if (nt_flush_buffers_file_ex_(
379
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)
380 1x return true;
381 }
382 2x return false;
383 }
384
385 } // namespace boost::corosio::detail
386
387 #endif // BOOST_COROSIO_HAS_IOCP
388
389 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_RANDOM_ACCESS_FILE_SERVICE_HPP
390