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

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