public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush private unsafe int ReadSpan(Span <byte> destination) { if (!CanRead) { ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); NativeOverlapped nativeOverlapped = GetNativeOverlappedForCurrentPosition(); int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination, true, &nativeOverlapped, out int errorCode); if (r == -1) { // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE) { r = 0; } else { if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) { ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle)); } throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); _filePosition += r; return(r); }
private unsafe ValueTask <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default) { if (!CanRead) { ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); // valueTaskSource is not null when: // - First time calling ReadAsync in buffered mode // - Second+ time calling ReadAsync, both buffered or unbuffered // - On buffered flush, when source memory is also the internal buffer // valueTaskSource is null when: // - First time calling ReadAsync in unbuffered mode ValueTaskSource valueTaskSource = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this); NativeOverlapped *intOverlapped = valueTaskSource.Configure(destination); // Calculate position in the file we should be at after the read is done long positionBefore = _filePosition; if (CanSeek) { long len = Length; if (positionBefore + destination.Length > len) { if (positionBefore <= len) { destination = destination.Slice(0, (int)(len - positionBefore)); } else { destination = default; } } // Now set the position to read from in the NativeOverlapped struct // For pipes, we should leave the offset fields set to 0. intOverlapped->OffsetLow = unchecked ((int)positionBefore); intOverlapped->OffsetHigh = (int)(positionBefore >> 32); // 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; } // queue an async ReadFile operation and pass in a packed overlapped int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, false, intOverlapped, out int errorCode); // ReadFile, the OS version, will return 0 on failure. But // my ReadFileNative wrapper returns -1. My wrapper will return // the following: // On error, r==-1. // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING // on async requests that completed sequentially, r==0 // You will NEVER RELIABLY be able to get the number of bytes // read back from this call when using overlapped structures! You must // not pass in a non-null lpNumBytesRead to ReadFile when using // overlapped structures! This is by design NT behavior. if (r == -1) { // For pipes, when they hit EOF, they will come here. if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE) { // Not an error, but EOF. AsyncFSCallback will NOT be // called. Call the user callback here. // We clear the overlapped status bit for this special case. // Failure to do so looks like we are freeing a pending overlapped later. intOverlapped->InternalLow = IntPtr.Zero; valueTaskSource.ReleaseNativeResource(); TryToReuse(valueTaskSource); return(new ValueTask <int>(0)); } else if (errorCode != Interop.Errors.ERROR_IO_PENDING) { if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. { _filePosition = positionBefore; } valueTaskSource.ReleaseNativeResource(); TryToReuse(valueTaskSource); if (errorCode == Interop.Errors.ERROR_HANDLE_EOF) { ThrowHelper.ThrowEndOfFileException(); } else { throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation valueTaskSource.RegisterForCancellation(cancellationToken); } } else { // Due to a workaround for a race condition in NT's ReadFile & // WriteFile routines, we will always be returning 0 from ReadFileNative // when we do async IO instead of the number of bytes read, // irregardless of whether the operation completed // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. } return(new ValueTask <int>(valueTaskSource, valueTaskSource.Version)); }
private unsafe Task <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default) { if (!CanRead) { ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); // Create and store async stream class library specific data in the async result FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, destination); NativeOverlapped * intOverlapped = completionSource.Overlapped; // Calculate position in the file we should be at after the read is done if (CanSeek) { long len = Length; // Make sure we are reading from the position that we think we are VerifyOSHandlePosition(); if (destination.Length > len - _filePosition) { if (_filePosition <= len) { destination = destination.Slice(0, (int)(len - _filePosition)); } else { destination = default; } } // Now set the position to read from in the NativeOverlapped struct // For pipes, we should leave the offset fields set to 0. intOverlapped->OffsetLow = unchecked ((int)_filePosition); intOverlapped->OffsetHigh = (int)(_filePosition >> 32); // When using overlapped IO, the OS is not supposed to // touch the file pointer location at all. We will adjust it // ourselves. This isn't threadsafe. // WriteFile should not update the file pointer when writing // in overlapped mode, according to MSDN. But it does update // the file pointer when writing to a UNC path! // So changed the code below to seek to an absolute // location, not a relative one. ReadFile seems consistent though. SeekCore(_fileHandle, destination.Length, SeekOrigin.Current); } // queue an async ReadFile operation and pass in a packed overlapped int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, intOverlapped, out int errorCode); // ReadFile, the OS version, will return 0 on failure. But // my ReadFileNative wrapper returns -1. My wrapper will return // the following: // On error, r==-1. // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING // on async requests that completed sequentially, r==0 // You will NEVER RELIABLY be able to get the number of bytes // read back from this call when using overlapped structures! You must // not pass in a non-null lpNumBytesRead to ReadFile when using // overlapped structures! This is by design NT behavior. if (r == -1) { // For pipes, when they hit EOF, they will come here. if (errorCode == ERROR_BROKEN_PIPE) { // Not an error, but EOF. AsyncFSCallback will NOT be // called. Call the user callback here. // We clear the overlapped status bit for this special case. // Failure to do so looks like we are freeing a pending overlapped later. intOverlapped->InternalLow = IntPtr.Zero; completionSource.SetCompletedSynchronously(0); } else if (errorCode != ERROR_IO_PENDING) { if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. { SeekCore(_fileHandle, 0, SeekOrigin.Current); } completionSource.ReleaseNativeResource(); if (errorCode == ERROR_HANDLE_EOF) { ThrowHelper.ThrowEndOfFileException(); } else { throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation completionSource.RegisterForCancellation(cancellationToken); } } else { // Due to a workaround for a race condition in NT's ReadFile & // WriteFile routines, we will always be returning 0 from ReadFileNative // when we do async IO instead of the number of bytes read, // irregardless of whether the operation completed // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. } return(completionSource.Task); }