From 7db34cc9f3fa9204fdd15af95444c851d6226546 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 21 Jul 2021 13:26:58 +0200 Subject: [PATCH] update position after the operation completes --- ...andle.OverlappedValueTaskSource.Windows.cs | 14 +++++++++-- .../src/System/IO/RandomAccess.Windows.cs | 5 ++-- .../AsyncWindowsFileStreamStrategy.cs | 23 ++++--------------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs index 367184e9a455e..2373fe39fb12c 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Diagnostics; using System.IO; +using System.IO.Strategies; using System.Threading; using System.Threading.Tasks.Sources; @@ -49,6 +50,7 @@ internal sealed unsafe class OverlappedValueTaskSource : IValueTaskSource, internal ManualResetValueTaskSourceCore _source; // mutable struct; do not make this readonly private NativeOverlapped* _overlapped; private CancellationTokenRegistration _cancellationRegistration; + private AsyncWindowsFileStreamStrategy? _strategy; /// /// 0 when the operation hasn't been scheduled, non-zero when either the operation has completed, /// in which case its value is a packed combination of the error code and number of bytes, or when @@ -74,9 +76,10 @@ internal static Exception GetIOError(int errorCode, string? path) ? ThrowHelper.CreateEndOfFileException() : Win32Marshal.GetExceptionForWin32Error(errorCode, path); - internal NativeOverlapped* PrepareForOperation(ReadOnlyMemory memory, long fileOffset) + internal NativeOverlapped* PrepareForOperation(ReadOnlyMemory memory, long fileOffset, AsyncWindowsFileStreamStrategy? strategy = null) { _result = 0; + _strategy = strategy; _memoryHandle = memory.Pin(); _overlapped = _fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(_preallocatedOverlapped); _overlapped->OffsetLow = (int)fileOffset; @@ -132,8 +135,9 @@ internal void RegisterForCancellation(CancellationToken cancellationToken) } } - internal void ReleaseResources() + private void ReleaseResources() { + _strategy = null; // Unpin any pinned buffer. _memoryHandle.Dispose(); @@ -187,6 +191,7 @@ private static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* internal void Complete(uint errorCode, uint numBytes) { + AsyncWindowsFileStreamStrategy? strategy = _strategy; ReleaseResources(); switch (errorCode) @@ -195,6 +200,11 @@ internal void Complete(uint errorCode, uint numBytes) case Interop.Errors.ERROR_BROKEN_PIPE: case Interop.Errors.ERROR_NO_DATA: case Interop.Errors.ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) + if (numBytes > 0) + { + // update the position before setting the result + strategy?.UpdatePosition(numBytes); + } // Success _source.SetResult((int)numBytes); break; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 8711ce23e9d2c..863a28952d4bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -242,14 +242,15 @@ internal static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + internal static unsafe (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) QueueAsyncReadFile(SafeFileHandle handle, Memory buffer, long fileOffset, + CancellationToken cancellationToken, AsyncWindowsFileStreamStrategy? strategy = null) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); try { - NativeOverlapped* nativeOverlapped = vts.PrepareForOperation(buffer, fileOffset); + NativeOverlapped* nativeOverlapped = vts.PrepareForOperation(buffer, fileOffset, strategy); Debug.Assert(vts._memoryHandle.Pointer != null); // Queue an async ReadFile operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index 0f59f6748a1a4..9114fa5acfec7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -35,27 +35,10 @@ private unsafe ValueTask ReadAsyncInternal(Memory destination, Cancel ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } - long positionBefore = _filePosition; - if (CanSeek) - { - long len = Length; - if (positionBefore + destination.Length > len) - { - destination = positionBefore <= len ? - destination.Slice(0, (int)(len - positionBefore)) : - default; - } - - // When using overlapped IO, the OS is not supposed to - // touch the file pointer location at all. We will adjust it - // ourselves, but only in memory. This isn't threadsafe. - _filePosition += destination.Length; - } - - (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) = RandomAccess.QueueAsyncReadFile(_fileHandle, destination, positionBefore, cancellationToken); + (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) = RandomAccess.QueueAsyncReadFile(_fileHandle, destination, _filePosition, cancellationToken, this); return vts != null ? new ValueTask(vts, vts.Version) - : (errorCode == 0) ? ValueTask.FromResult(0) : ValueTask.FromException(HandleIOError(positionBefore, errorCode)); + : (errorCode == 0) ? ValueTask.FromResult(0) : ValueTask.FromException(SafeFileHandle.OverlappedValueTaskSource.GetIOError(errorCode, _fileHandle.Path)); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -149,5 +132,7 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); + + internal void UpdatePosition(uint numBytes) => _filePosition += numBytes; } }