From 44b0676a74f02ae249d5b32e7e8ccce753332343 Mon Sep 17 00:00:00 2001 From: gycherish Date: Tue, 11 Aug 2026 08:02:59 -0700 Subject: [PATCH] Fix stream_file position accounting for multi-buffer operations on Windows. win_iocp_file_service advances the file position by the size of the whole buffer sequence, but win_iocp_handle_service submits only the sequence's first non-empty buffer, ReadFile and WriteFile taking one buffer each. The position therefore moves past data that has not been transferred, and the composed read/write operations resume from there: a scatter write leaves a hole in the file, and a scatter read reports end_of_file with the later buffers unfilled. Advance by the buffer that is actually submitted. That size is known before the operation is initiated, so the accounting stays where it is today and no completion handler is involved. A single-buffer sequence is unaffected, its first buffer being the whole sequence. Only the Windows backend maintains the position itself, IOCP requiring an explicit offset per operation; io_uring and the reactive backends leave it to the kernel and pass the whole sequence to writev in one operation. --- include/asio/detail/win_iocp_file_service.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/include/asio/detail/win_iocp_file_service.hpp b/include/asio/detail/win_iocp_file_service.hpp index 7072dbc3b4..40f1262b3e 100644 --- a/include/asio/detail/win_iocp_file_service.hpp +++ b/include/asio/detail/win_iocp_file_service.hpp @@ -171,7 +171,11 @@ class win_iocp_file_service : const ConstBufferSequence& buffers, asio::error_code& ec) { uint64_t offset = impl.offset_; - impl.offset_ += asio::buffer_size(buffers); + // The handle service submits the first non-empty buffer of the sequence and + // no more, so the position advances by that buffer alone. The composed + // operations resume from the new position with the buffers left over. + impl.offset_ += buffer_sequence_adapter::first(buffers).size(); return handle_service_.write_some_at(impl, offset, buffers, ec); } @@ -183,7 +187,8 @@ class win_iocp_file_service : Handler& handler, const IoExecutor& io_ex) { uint64_t offset = impl.offset_; - impl.offset_ += asio::buffer_size(buffers); + impl.offset_ += buffer_sequence_adapter::first(buffers).size(); handle_service_.async_write_some_at(impl, offset, buffers, handler, io_ex); } @@ -212,7 +217,8 @@ class win_iocp_file_service : const MutableBufferSequence& buffers, asio::error_code& ec) { uint64_t offset = impl.offset_; - impl.offset_ += asio::buffer_size(buffers); + impl.offset_ += buffer_sequence_adapter::first(buffers).size(); return handle_service_.read_some_at(impl, offset, buffers, ec); } @@ -225,7 +231,8 @@ class win_iocp_file_service : Handler& handler, const IoExecutor& io_ex) { uint64_t offset = impl.offset_; - impl.offset_ += asio::buffer_size(buffers); + impl.offset_ += buffer_sequence_adapter::first(buffers).size(); handle_service_.async_read_some_at(impl, offset, buffers, handler, io_ex); }