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

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