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

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