From c0ded0099fac9eb5bffe1c5a46ca4bdeb469d664 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 12:31:34 +0100 Subject: [PATCH 001/100] rename FileStream -> FileStreamImpl FILES --- .../src/System.Private.CoreLib.Shared.projitems | 11 ++++++----- ...eStream.Lock.OSX.cs => FileStreamImpl.Lock.OSX.cs} | 0 ...tream.Lock.Unix.cs => FileStreamImpl.Lock.Unix.cs} | 0 .../IO/{FileStream.Unix.cs => FileStreamImpl.Unix.cs} | 0 .../{FileStream.Win32.cs => FileStreamImpl.Win32.cs} | 0 ...ileStream.Windows.cs => FileStreamImpl.Windows.cs} | 0 .../System/IO/{FileStream.cs => FileStreamImpl.cs} | 0 7 files changed, 6 insertions(+), 5 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStream.Lock.OSX.cs => FileStreamImpl.Lock.OSX.cs} (100%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStream.Lock.Unix.cs => FileStreamImpl.Lock.Unix.cs} (100%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStream.Unix.cs => FileStreamImpl.Unix.cs} (100%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStream.Win32.cs => FileStreamImpl.Win32.cs} (100%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStream.Windows.cs => FileStreamImpl.Windows.cs} (100%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStream.cs => FileStreamImpl.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 642ff0d671a92..79639fa934768 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -395,6 +395,7 @@ + @@ -1611,8 +1612,8 @@ - - + + @@ -1816,9 +1817,9 @@ - - - + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.OSX.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Win32.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs From 0fe40d1ccfd82a05e6d71bebb515f202b7234e50 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 12:34:02 +0100 Subject: [PATCH 002/100] rename FileStream -> FileStreamImpl CLASS, make it internal and sealed --- .../src/System/IO/FileStreamCompletionSource.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.OSX.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.Unix.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Windows.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index 226b5732e0a09..efe7220fd893a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs index 315f5a5997de6..a7387ec5cad4b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { private static void LockInternal(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs index 1869552a55471..5ceb2f86faba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index 38dc6d2d01174..188db92c7b02f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { /// File mode. private FileMode _mode; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs index aee2562cc9dde..29b19e2468e03 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs @@ -6,7 +6,7 @@ namespace System.IO { - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index 6e7b5480509e0..39a94fee28fe7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -39,7 +39,7 @@ namespace System.IO { - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 415419f85b4bb..4d7f3ac75d590 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -10,7 +10,7 @@ namespace System.IO { - public partial class FileStream : Stream + internal sealed partial class FileStreamImpl : Stream { private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; From e352313aeb4519b91375c0576833a13bc91ff70b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 12:46:45 +0100 Subject: [PATCH 003/100] restore orgiginal FileStream --- .../src/System/IO/FileStream.cs | 911 ++++++++++++++++++ 1 file changed, 911 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs new file mode 100644 index 0000000000000..415419f85b4bb --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -0,0 +1,911 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO +{ + public partial class FileStream : Stream + { + private const FileShare DefaultShare = FileShare.Read; + private const bool DefaultIsAsync = false; + internal const int DefaultBufferSize = 4096; + + private byte[]? _buffer; + private int _bufferLength; + private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws + + /// Whether the file is opened for reading, writing, or both. + private readonly FileAccess _access; + + /// The path to the opened file. + private readonly string? _path; + + /// The next available byte to be read from the _buffer. + private int _readPos; + + /// The number of valid bytes in _buffer. + private int _readLength; + + /// The next location in which a write should occur to the buffer. + private int _writePos; + + /// + /// Whether asynchronous read/write/flush operations should be performed using async I/O. + /// On Windows FileOptions.Asynchronous controls how the file handle is configured, + /// and then as a result how operations are issued against that file handle. On Unix, + /// there isn't any distinction around how file descriptors are created for async vs + /// sync, but we still differentiate how the operations are issued in order to provide + /// similar behavioral semantics and performance characteristics as on Windows. On + /// Windows, if non-async, async read/write requests just delegate to the base stream, + /// and no attempt is made to synchronize between sync and async operations on the stream; + /// if async, then async read/write requests are implemented specially, and sync read/write + /// requests are coordinated with async ones by implementing the sync ones over the async + /// ones. On Unix, we do something similar. If non-async, async read/write requests just + /// delegate to the base stream, and no attempt is made to synchronize. If async, we use + /// a semaphore to coordinate both sync and async operations. + /// + private readonly bool _useAsyncIO; + + /// cached task for read ops that complete synchronously + private Task? _lastSynchronouslyCompletedTask; + + /// + /// Currently cached position in the stream. This should always mirror the underlying file's actual position, + /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which + /// point we attempt to error out. + /// + private long _filePosition; + + /// Whether the file stream's handle has been exposed. + private bool _exposedHandle; + + /// Caches whether Serialization Guard has been disabled for file writes + private static int s_cachedSerializationSwitch; + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access) + : this(handle, access, true, DefaultBufferSize, false) + { + } + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access, bool ownsHandle) + : this(handle, access, ownsHandle, DefaultBufferSize, false) + { + } + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) + : this(handle, access, ownsHandle, bufferSize, false) + { + } + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) + { + SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); + try + { + ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync); + } + catch + { + // We don't want to take ownership of closing passed in handles + // *unless* the constructor completes successfully. + GC.SuppressFinalize(safeHandle); + + // This would also prevent Close from being called, but is unnecessary + // as we've removed the object from the finalizer queue. + // + // safeHandle.SetHandleAsInvalid(); + throw; + } + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _useAsyncIO = isAsync; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = safeHandle; + } + + public FileStream(SafeFileHandle handle, FileAccess access) + : this(handle, access, DefaultBufferSize) + { + } + + public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) + : this(handle, access, bufferSize, GetDefaultIsAsync(handle)) + { + } + + private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + { + if (handle.IsInvalid) + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + if (handle.IsClosed) + throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault()) + throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); + + _exposedHandle = true; + _bufferLength = bufferSize; + + InitFromHandle(handle, access, isAsync); + } + + public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + { + ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _useAsyncIO = isAsync; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = handle; + } + + public FileStream(string path, FileMode mode) : + this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access) : + this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : + this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) : + this(path, mode, access, share, bufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : + this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + { + if (path == null) + throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); + if (path.Length == 0) + throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); + + // don't include inheritable in our bounds check for share + FileShare tempshare = share & ~FileShare.Inheritable; + string? badArg = null; + + if (mode < FileMode.CreateNew || mode > FileMode.Append) + badArg = nameof(mode); + else if (access < FileAccess.Read || access > FileAccess.ReadWrite) + badArg = nameof(access); + else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) + badArg = nameof(share); + + if (badArg != null) + throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); + + // NOTE: any change to FileOptions enum needs to be matched here in the error validation + if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) + throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); + + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + // Write access validation + if ((access & FileAccess.Write) == 0) + { + if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) + { + // No write access, mode and access disagree but flag access since mode comes first + throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access)); + } + } + + if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) + throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); + + string fullPath = Path.GetFullPath(path); + + _path = fullPath; + _access = access; + _bufferLength = bufferSize; + + if ((options & FileOptions.Asynchronous) != 0) + _useAsyncIO = true; + + if ((access & FileAccess.Write) == FileAccess.Write) + { + SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); + } + + _fileHandle = OpenHandle(mode, share, options); + + try + { + Init(mode, share, path); + } + catch + { + // If anything goes wrong while setting up the stream, make sure we deterministically dispose + // of the opened handle. + _fileHandle.Dispose(); + _fileHandle = null!; + throw; + } + } + + [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] + public virtual IntPtr Handle => SafeFileHandle.DangerousGetHandle(); + + public virtual void Lock(long position, long length) + { + if (position < 0 || length < 0) + { + throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + LockInternal(position, length); + } + + public virtual void Unlock(long position, long length) + { + if (position < 0 || length < 0) + { + throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + UnlockInternal(position, length); + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Flush() which a subclass might have overridden. To be safe + // we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Flush) when we are not sure. + if (GetType() != typeof(FileStream)) + return base.FlushAsync(cancellationToken); + + return FlushAsyncInternal(cancellationToken); + } + + /// Asynchronously clears all buffers for this stream, causing any buffered data to be written to the underlying device. + /// The token to monitor for cancellation requests. + /// A task that represents the asynchronous flush operation. + private Task FlushAsyncInternal(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + // TODO: https://github.com/dotnet/runtime/issues/27643 (stop doing this synchronous work!!). + // The always synchronous data transfer between the OS and the internal buffer is intentional + // because this is needed to allow concurrent async IO requests. Concurrent data transfer + // between the OS and the internal buffer will result in race conditions. Since FlushWrite and + // FlushRead modify internal state of the stream and transfer data between the OS and the + // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers + // asynchronously because it doesn't modify any internal state of the stream and is potentially + // a long running process. + try + { + FlushInternalBuffer(); + } + catch (Exception e) + { + return Task.FromException(e); + } + + return Task.CompletedTask; + } + + public override int Read(byte[] buffer, int offset, int count) + { + ValidateReadWriteArgs(buffer, offset, count); + return _useAsyncIO ? + ReadAsyncTask(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult() : + ReadSpan(new Span(buffer, offset, count)); + } + + public override int Read(Span buffer) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + return ReadSpan(buffer); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) + // overload being introduced. In that case, this Read(Span) overload should use the behavior + // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to + // Read(byte[],int,int), which will do the right thing if we're in async mode. + return base.Read(buffer); + } + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + + if (GetType() != typeof(FileStream)) + { + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Read() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. + return base.ReadAsync(buffer, offset, count, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + if (IsClosed) + throw Error.GetFileNotOpen(); + + if (!_useAsyncIO) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Read is invoked asynchronously. But we can do so using the base Stream's internal helper + // that bypasses delegating to BeginRead, since we already know this is FileStream rather + // than something derived from it and what our BeginRead implementation is going to do. + return (Task)base.BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + } + + return ReadAsyncTask(buffer, offset, count, cancellationToken); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + if (GetType() != typeof(FileStream)) + { + // If this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.ReadAsync(buffer, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + if (!_useAsyncIO) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Read is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's + // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream + // rather than something derived from it and what our BeginRead implementation is going to do. + return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? + new ValueTask((Task)base.BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + base.ReadAsync(buffer, cancellationToken); + } + + Task? t = ReadAsyncInternal(buffer, cancellationToken, out int synchronousResult); + return t != null ? + new ValueTask(t) : + new ValueTask(synchronousResult); + } + + private Task ReadAsyncTask(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + Task? t = ReadAsyncInternal(new Memory(buffer, offset, count), cancellationToken, out int synchronousResult); + + if (t == null) + { + t = _lastSynchronouslyCompletedTask; + Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully"); + + if (t == null || t.Result != synchronousResult) + { + _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult); + } + } + + return t; + } + + public override void Write(byte[] buffer, int offset, int count) + { + ValidateReadWriteArgs(buffer, offset, count); + if (_useAsyncIO) + { + WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask().GetAwaiter().GetResult(); + } + else + { + WriteSpan(new ReadOnlySpan(buffer, offset, count)); + } + } + + public override void Write(ReadOnlySpan buffer) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + WriteSpan(buffer); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) + // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior + // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to + // Write(byte[],int,int), which will do the right thing if we're in async mode. + base.Write(buffer); + } + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + + if (GetType() != typeof(FileStream)) + { + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. + return base.WriteAsync(buffer, offset, count, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + if (IsClosed) + throw Error.GetFileNotOpen(); + + if (!_useAsyncIO) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Write is invoked asynchronously. But we can do so using the base Stream's internal helper + // that bypasses delegating to BeginWrite, since we already know this is FileStream rather + // than something derived from it and what our BeginWrite implementation is going to do. + return (Task)base.BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + } + + return WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + if (GetType() != typeof(FileStream)) + { + // If this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.WriteAsync(buffer, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + if (!_useAsyncIO) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Write is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's + // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream + // rather than something derived from it and what our BeginWrite implementation is going to do. + return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? + new ValueTask((Task)BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + base.WriteAsync(buffer, cancellationToken); + } + + return WriteAsyncInternal(buffer, cancellationToken); + } + + /// + /// Clears buffers for this stream and causes any buffered data to be written to the file. + /// + public override void Flush() + { + // Make sure that we call through the public virtual API + Flush(flushToDisk: false); + } + + /// + /// Clears buffers for this stream, and if is true, + /// causes any buffered data to be written to the file. + /// + public virtual void Flush(bool flushToDisk) + { + if (IsClosed) throw Error.GetFileNotOpen(); + + FlushInternalBuffer(); + + if (flushToDisk && CanWrite) + { + FlushOSBuffer(); + } + } + + /// Gets a value indicating whether the current stream supports reading. + public override bool CanRead => !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; + + /// Gets a value indicating whether the current stream supports writing. + public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; + + /// Validates arguments to Read and Write and throws resulting exceptions. + /// The buffer to read from or write to. + /// The zero-based offset into the buffer. + /// The maximum number of bytes to read or write. + private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) + { + ValidateBufferArguments(buffer, offset, count); + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + } + + /// Sets the length of this stream to the given value. + /// The new length of the stream. + public override void SetLength(long value) + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + if (!CanSeek) + throw Error.GetSeekNotSupported(); + if (!CanWrite) + throw Error.GetWriteNotSupported(); + + SetLengthInternal(value); + } + + public virtual SafeFileHandle SafeFileHandle + { + get + { + Flush(); + _exposedHandle = true; + return _fileHandle; + } + } + + /// Gets the path that was passed to the constructor. + public virtual string Name => _path ?? SR.IO_UnknownFileName; + + /// Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously. + public virtual bool IsAsync => _useAsyncIO; + + /// Gets the length of the stream in bytes. + public override long Length + { + get + { + if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); + if (!CanSeek) throw Error.GetSeekNotSupported(); + return GetLengthInternal(); + } + } + + /// + /// Verify that the actual position of the OS's handle equals what we expect it to. + /// This will fail if someone else moved the UnixFileStream's handle or if + /// our position updating code is incorrect. + /// + private void VerifyOSHandlePosition() + { + bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it +#if DEBUG + verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be +#endif + if (verifyPosition && CanSeek) + { + long oldPos = _filePosition; // SeekCore will override the current _position, so save it now + long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); + if (oldPos != curPos) + { + // For reads, this is non-fatal but we still could have returned corrupted + // data in some cases, so discard the internal buffer. For writes, + // this is a problem; discard the buffer and error out. + _readPos = _readLength = 0; + if (_writePos > 0) + { + _writePos = 0; + throw new IOException(SR.IO_FileStreamHandlePosition); + } + } + } + } + + /// Verifies that state relating to the read/write buffer is consistent. + [Conditional("DEBUG")] + private void AssertBufferInvariants() + { + // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength + Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength); + + // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength + Debug.Assert(0 <= _writePos && _writePos <= _bufferLength); + + // Read buffering and write buffering can't both be active + Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0); + } + + /// Validates that we're ready to read from the stream. + private void PrepareForReading() + { + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + if (_readLength == 0 && !CanRead) + throw Error.GetReadNotSupported(); + + AssertBufferInvariants(); + } + + /// Gets or sets the position within the current stream + public override long Position + { + get + { + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + + if (!CanSeek) + throw Error.GetSeekNotSupported(); + + AssertBufferInvariants(); + VerifyOSHandlePosition(); + + // We may have read data into our buffer from the handle, such that the handle position + // is artificially further along than the consumer's view of the stream's position. + // Thus, when reading, our position is really starting from the handle position negatively + // offset by the number of bytes in the buffer and positively offset by the number of + // bytes into that buffer we've read. When writing, both the read length and position + // must be zero, and our position is just the handle position offset positive by how many + // bytes we've written into the buffer. + return (_filePosition - _readLength) + _readPos + _writePos; + } + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + Seek(value, SeekOrigin.Begin); + } + } + + internal virtual bool IsClosed => _fileHandle.IsClosed; + + private static bool IsIoRelatedException(Exception e) => + // These all derive from IOException + // DirectoryNotFoundException + // DriveNotFoundException + // EndOfStreamException + // FileLoadException + // FileNotFoundException + // PathTooLongException + // PipeException + e is IOException || + // Note that SecurityException is only thrown on runtimes that support CAS + // e is SecurityException || + e is UnauthorizedAccessException || + e is NotSupportedException || + (e is ArgumentException && !(e is ArgumentNullException)); + + /// + /// Gets the array used for buffering reading and writing. + /// If the array hasn't been allocated, this will lazily allocate it. + /// + /// The buffer. + private byte[] GetBuffer() + { + Debug.Assert(_buffer == null || _buffer.Length == _bufferLength); + if (_buffer == null) + { + _buffer = new byte[_bufferLength]; + OnBufferAllocated(); + } + + return _buffer; + } + + partial void OnBufferAllocated(); + + /// + /// Flushes the internal read/write buffer for this stream. If write data has been buffered, + /// that data is written out to the underlying file. Or if data has been buffered for + /// reading from the stream, the data is dumped and our position in the underlying file + /// is rewound as necessary. This does not flush the OS buffer. + /// + private void FlushInternalBuffer() + { + AssertBufferInvariants(); + if (_writePos > 0) + { + FlushWriteBuffer(); + } + else if (_readPos < _readLength && CanSeek) + { + FlushReadBuffer(); + } + } + + /// Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary. + private void FlushReadBuffer() + { + // Reading is done by blocks from the file, but someone could read + // 1 byte from the buffer then write. At that point, the OS's file + // pointer is out of sync with the stream's position. All write + // functions should call this function to preserve the position in the file. + + AssertBufferInvariants(); + Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!"); + + int rewind = _readPos - _readLength; + if (rewind != 0) + { + Debug.Assert(CanSeek, "FileStream will lose buffered read data now."); + SeekCore(_fileHandle, rewind, SeekOrigin.Current); + } + _readPos = _readLength = 0; + } + + /// + /// Reads a byte from the file stream. Returns the byte cast to an int + /// or -1 if reading from the end of the stream. + /// + public override int ReadByte() + { + PrepareForReading(); + + byte[] buffer = GetBuffer(); + if (_readPos == _readLength) + { + FlushWriteBuffer(); + _readLength = FillReadBufferForReadByte(); + _readPos = 0; + if (_readLength == 0) + { + return -1; + } + } + + return buffer[_readPos++]; + } + + /// + /// Writes a byte to the current position in the stream and advances the position + /// within the stream by one byte. + /// + /// The byte to write to the stream. + public override void WriteByte(byte value) + { + PrepareForWriting(); + + // Flush the write buffer if it's full + if (_writePos == _bufferLength) + FlushWriteBufferForWriteByte(); + + // We now have space in the buffer. Store the byte. + GetBuffer()[_writePos++] = value; + } + + /// + /// Validates that we're ready to write to the stream, + /// including flushing a read buffer if necessary. + /// + private void PrepareForWriting() + { + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + + // Make sure we're good to write. We only need to do this if there's nothing already + // in our write buffer, since if there is something in the buffer, we've already done + // this checking and flushing. + if (_writePos == 0) + { + if (!CanWrite) throw Error.GetWriteNotSupported(); + FlushReadBuffer(); + Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); + } + } + + ~FileStream() + { + // Preserved for compatibility since FileStream has defined a + // finalizer in past releases and derived classes may depend + // on Dispose(false) call. + Dispose(false); + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + { + ValidateBufferArguments(buffer, offset, count); + if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + + if (!IsAsync) + return base.BeginRead(buffer, offset, count, callback, state); + else + return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + { + ValidateBufferArguments(buffer, offset, count); + if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + + if (!IsAsync) + return base.BeginWrite(buffer, offset, count, callback, state); + else + return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + if (!IsAsync) + return base.EndRead(asyncResult); + else + return TaskToApm.End(asyncResult); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + if (!IsAsync) + base.EndWrite(asyncResult); + else + TaskToApm.End(asyncResult); + } + } +} From 550e63f2351ae649eb41c28448f78f9fbb0df477 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 12:49:18 +0100 Subject: [PATCH 004/100] ctors --- .../src/System/IO/FileStream.cs | 129 +----------------- .../src/System/IO/FileStreamImpl.cs | 55 +------- 2 files changed, 8 insertions(+), 176 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 415419f85b4bb..f8d47e164b136 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -12,6 +12,8 @@ namespace System.IO { public partial class FileStream : Stream { + private readonly Stream _actualImplementation; + private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; internal const int DefaultBufferSize = 4096; @@ -89,32 +91,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { - SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); - try - { - ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync); - } - catch - { - // We don't want to take ownership of closing passed in handles - // *unless* the constructor completes successfully. - GC.SuppressFinalize(safeHandle); - - // This would also prevent Close from being called, but is unnecessary - // as we've removed the object from the finalizer queue. - // - // safeHandle.SetHandleAsInvalid(); - throw; - } - - // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, - // but we can't as they're readonly. - _access = access; - _useAsyncIO = isAsync; - - // As the handle was passed in, we must set the handle field at the very end to - // avoid the finalizer closing the handle when we throw errors. - _fileHandle = safeHandle; + _actualImplementation = new FileStreamImpl(handle, access, ownsHandle, bufferSize, isAsync); } public FileStream(SafeFileHandle handle, FileAccess access) @@ -127,39 +104,9 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) { } - private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - { - if (handle.IsInvalid) - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); - - if (handle.IsClosed) - throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault()) - throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); - - _exposedHandle = true; - _bufferLength = bufferSize; - - InitFromHandle(handle, access, isAsync); - } - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); - - // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, - // but we can't as they're readonly. - _access = access; - _useAsyncIO = isAsync; - - // As the handle was passed in, we must set the handle field at the very end to - // avoid the finalizer closing the handle when we throw errors. - _fileHandle = handle; + _actualImplementation = new FileStreamImpl(handle, access, bufferSize, isAsync); } public FileStream(string path, FileMode mode) : @@ -184,73 +131,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { - if (path == null) - throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); - if (path.Length == 0) - throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - - // don't include inheritable in our bounds check for share - FileShare tempshare = share & ~FileShare.Inheritable; - string? badArg = null; - - if (mode < FileMode.CreateNew || mode > FileMode.Append) - badArg = nameof(mode); - else if (access < FileAccess.Read || access > FileAccess.ReadWrite) - badArg = nameof(access); - else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) - badArg = nameof(share); - - if (badArg != null) - throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); - - // NOTE: any change to FileOptions enum needs to be matched here in the error validation - if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) - throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); - - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); - - // Write access validation - if ((access & FileAccess.Write) == 0) - { - if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) - { - // No write access, mode and access disagree but flag access since mode comes first - throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access)); - } - } - - if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) - throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); - - string fullPath = Path.GetFullPath(path); - - _path = fullPath; - _access = access; - _bufferLength = bufferSize; - - if ((options & FileOptions.Asynchronous) != 0) - _useAsyncIO = true; - - if ((access & FileAccess.Write) == FileAccess.Write) - { - SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); - } - - _fileHandle = OpenHandle(mode, share, options); - - try - { - Init(mode, share, path); - } - catch - { - // If anything goes wrong while setting up the stream, make sure we deterministically dispose - // of the opened handle. - _fileHandle.Dispose(); - _fileHandle = null!; - throw; - } + _actualImplementation = new FileStreamImpl(path, mode, access, share, bufferSize, options); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 4d7f3ac75d590..decf02e171958 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -68,26 +68,7 @@ internal sealed partial class FileStreamImpl : Stream /// Caches whether Serialization Guard has been disabled for file writes private static int s_cachedSerializationSwitch; - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access) - : this(handle, access, true, DefaultBufferSize, false) - { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle) - : this(handle, access, ownsHandle, DefaultBufferSize, false) - { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) - : this(handle, access, ownsHandle, bufferSize, false) - { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) + internal FileStreamImpl(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); try @@ -117,16 +98,6 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS _fileHandle = safeHandle; } - public FileStream(SafeFileHandle handle, FileAccess access) - : this(handle, access, DefaultBufferSize) - { - } - - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, GetDefaultIsAsync(handle)) - { - } - private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { if (handle.IsInvalid) @@ -148,7 +119,7 @@ private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, InitFromHandle(handle, access, isAsync); } - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); @@ -162,27 +133,7 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool _fileHandle = handle; } - public FileStream(string path, FileMode mode) : - this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync) - { } - - public FileStream(string path, FileMode mode, FileAccess access) : - this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync) - { } - - public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : - this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync) - { } - - public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) : - this(path, mode, access, share, bufferSize, DefaultIsAsync) - { } - - public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : - this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None) - { } - - public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { if (path == null) throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); From 62d863c937fe0d7e26dd89e2de0093f2151d3528 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 12:58:40 +0100 Subject: [PATCH 005/100] introduce new abstract class to have Lock, Unlock and Handle methods to call, implement these methods --- .../src/System/IO/FileStream.cs | 34 +++---------------- .../IO/FileStreamCompletionSource.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.OSX.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Windows.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 18 +++++++--- 8 files changed, 23 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index f8d47e164b136..a394c14a5ae31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -12,7 +12,7 @@ namespace System.IO { public partial class FileStream : Stream { - private readonly Stream _actualImplementation; + private readonly LockableStream _actualImplementation; private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; @@ -135,37 +135,11 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle => SafeFileHandle.DangerousGetHandle(); + public virtual IntPtr Handle => _actualImplementation.Handle; - public virtual void Lock(long position, long length) - { - if (position < 0 || length < 0) - { - throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); - } - - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - - LockInternal(position, length); - } + public virtual void Lock(long position, long length) => _actualImplementation.Lock(position, length); - public virtual void Unlock(long position, long length) - { - if (position < 0 || length < 0) - { - throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); - } - - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - - UnlockInternal(position, length); - } + public virtual void Unlock(long position, long length) => _actualImplementation.Unlock(position, length); public override Task FlushAsync(CancellationToken cancellationToken) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index efe7220fd893a..c6a691dd6d8b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : Stream + internal sealed partial class FileStreamImpl : LockableStream { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs index a7387ec5cad4b..e2123a9d758ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : Stream + internal sealed partial class FileStreamImpl : LockableStream { private static void LockInternal(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs index 5ceb2f86faba2..d83c6010edc10 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : Stream + internal sealed partial class FileStreamImpl : LockableStream { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index 188db92c7b02f..b3c552e984881 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class FileStreamImpl : Stream + internal sealed partial class FileStreamImpl : LockableStream { /// File mode. private FileMode _mode; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs index 29b19e2468e03..e9ad4f1711441 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs @@ -6,7 +6,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : Stream + internal sealed partial class FileStreamImpl : LockableStream { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index 39a94fee28fe7..ab809a2712c9a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -39,7 +39,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : Stream + internal sealed partial class FileStreamImpl : LockableStream { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index decf02e171958..d7c41d77c1b36 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -10,7 +10,16 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : Stream + internal abstract class LockableStream : Stream + { + internal abstract IntPtr Handle { get; } + + internal abstract void Lock(long position, long length); + + internal abstract void Unlock(long position, long length); + } + + internal sealed partial class FileStreamImpl : LockableStream { private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; @@ -204,10 +213,9 @@ internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare } } - [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle => SafeFileHandle.DangerousGetHandle(); + internal override IntPtr Handle => SafeFileHandle.DangerousGetHandle(); - public virtual void Lock(long position, long length) + internal override void Lock(long position, long length) { if (position < 0 || length < 0) { @@ -222,7 +230,7 @@ public virtual void Lock(long position, long length) LockInternal(position, length); } - public virtual void Unlock(long position, long length) + internal override void Unlock(long position, long length) { if (position < 0 || length < 0) { From 14d2aa0d09da5293029fd7e8cca8f898b4f52bcc Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:00:29 +0100 Subject: [PATCH 006/100] implement FlushAsync --- .../src/System/IO/FileStream.cs | 36 +------------------ .../src/System/IO/FileStreamImpl.cs | 15 -------- 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index a394c14a5ae31..5da19c1cb51d3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -150,41 +150,7 @@ public override Task FlushAsync(CancellationToken cancellationToken) if (GetType() != typeof(FileStream)) return base.FlushAsync(cancellationToken); - return FlushAsyncInternal(cancellationToken); - } - - /// Asynchronously clears all buffers for this stream, causing any buffered data to be written to the underlying device. - /// The token to monitor for cancellation requests. - /// A task that represents the asynchronous flush operation. - private Task FlushAsyncInternal(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - - // TODO: https://github.com/dotnet/runtime/issues/27643 (stop doing this synchronous work!!). - // The always synchronous data transfer between the OS and the internal buffer is intentional - // because this is needed to allow concurrent async IO requests. Concurrent data transfer - // between the OS and the internal buffer will result in race conditions. Since FlushWrite and - // FlushRead modify internal state of the stream and transfer data between the OS and the - // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers - // asynchronously because it doesn't modify any internal state of the stream and is potentially - // a long running process. - try - { - FlushInternalBuffer(); - } - catch (Exception e) - { - return Task.FromException(e); - } - - return Task.CompletedTask; + return _actualImplementation.FlushAsync(cancellationToken); } public override int Read(byte[] buffer, int offset, int count) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index d7c41d77c1b36..edce79d2ac0ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -246,21 +246,6 @@ internal override void Unlock(long position, long length) } public override Task FlushAsync(CancellationToken cancellationToken) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Flush() which a subclass might have overridden. To be safe - // we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Flush) when we are not sure. - if (GetType() != typeof(FileStream)) - return base.FlushAsync(cancellationToken); - - return FlushAsyncInternal(cancellationToken); - } - - /// Asynchronously clears all buffers for this stream, causing any buffered data to be written to the underlying device. - /// The token to monitor for cancellation requests. - /// A task that represents the asynchronous flush operation. - private Task FlushAsyncInternal(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { From 796d7a35446632ca6117df1d960c1d745b30e9d6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:08:59 +0100 Subject: [PATCH 007/100] add all virtual methods to the base abstrac class (and rename it) --- .../System/IO/FileStreamCompletionSource.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.OSX.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Windows.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 14 ++++++++++++-- 7 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index c6a691dd6d8b4..ccd0f1ec9a2c2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs index e2123a9d758ac..7cfbb71cd32ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { private static void LockInternal(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs index d83c6010edc10..379dc679f62cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index b3c552e984881..bdf35825743a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { /// File mode. private FileMode _mode; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs index e9ad4f1711441..48c1c5f992d31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs @@ -6,7 +6,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index ab809a2712c9a..9c7261329642b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -39,7 +39,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index edce79d2ac0ae..39f34dd1a3c90 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -10,16 +10,26 @@ namespace System.IO { - internal abstract class LockableStream : Stream + internal abstract class FileStreamImplBase : Stream { + internal abstract bool IsAsync { get; } + + internal abstract string Name { get; } + internal abstract IntPtr Handle { get; } + internal abstract SafeFileHandle SafeFileHandle { get; } + + internal abstract bool IsClosed { get; } + internal abstract void Lock(long position, long length); internal abstract void Unlock(long position, long length); + + internal abstract void Flush(bool flushToDisk); } - internal sealed partial class FileStreamImpl : LockableStream + internal sealed partial class FileStreamImpl : FileStreamImplBase { private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; From 2108bd61544fe0f8a405918597b9a4d61df9e87e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:11:47 +0100 Subject: [PATCH 008/100] implement Read(byte[] buffer, int offset, int count) --- .../src/System/IO/FileStream.cs | 16 +++----------- .../src/System/IO/FileStreamImpl.cs | 21 +++++-------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 5da19c1cb51d3..8d60963bc4357 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -153,23 +153,13 @@ public override Task FlushAsync(CancellationToken cancellationToken) return _actualImplementation.FlushAsync(cancellationToken); } - public override int Read(byte[] buffer, int offset, int count) - { - ValidateReadWriteArgs(buffer, offset, count); - return _useAsyncIO ? - ReadAsyncTask(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult() : - ReadSpan(new Span(buffer, offset, count)); - } + public override int Read(byte[] buffer, int offset, int count) => _actualImplementation.Read(buffer, offset, count); public override int Read(Span buffer) { - if (GetType() == typeof(FileStream) && !_useAsyncIO) + if (GetType() == typeof(FileStream) && !_actualImplementation.IsAsync) { - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - return ReadSpan(buffer); + return _actualImplementation.Read(buffer); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 39f34dd1a3c90..3c4284b9b266b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -296,24 +296,13 @@ public override int Read(byte[] buffer, int offset, int count) public override int Read(Span buffer) { - if (GetType() == typeof(FileStream) && !_useAsyncIO) - { - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - return ReadSpan(buffer); - } - else + Debug.Assert(!_useAsyncIO); + + if (_fileHandle.IsClosed) { - // This type is derived from FileStream and/or the stream is in async mode. If this is a - // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) - // overload being introduced. In that case, this Read(Span) overload should use the behavior - // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the - // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to - // Read(byte[],int,int), which will do the right thing if we're in async mode. - return base.Read(buffer); + throw Error.GetFileNotOpen(); } + return ReadSpan(buffer); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) From 3cc6d07d1491ffae73472a6d4eb42a730d3ddfd0 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:14:28 +0100 Subject: [PATCH 009/100] ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) --- .../src/System/IO/FileStream.cs | 13 ++----------- .../src/System/IO/FileStreamImpl.cs | 17 ----------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 8d60963bc4357..738db4c88c00d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -189,19 +189,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); - if (IsClosed) + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Read is invoked asynchronously. But we can do so using the base Stream's internal helper - // that bypasses delegating to BeginRead, since we already know this is FileStream rather - // than something derived from it and what our BeginRead implementation is going to do. - return (Task)base.BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); - } - - return ReadAsyncTask(buffer, offset, count, cancellationToken); + return _actualImplementation.ReadAsync(buffer, offset, count, cancellationToken); } public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 3c4284b9b266b..3fa43df47d06f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -307,23 +307,6 @@ public override int Read(Span buffer) public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - ValidateBufferArguments(buffer, offset, count); - - if (GetType() != typeof(FileStream)) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() which a subclass might have overridden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. - return base.ReadAsync(buffer, offset, count, cancellationToken); - } - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - if (IsClosed) - throw Error.GetFileNotOpen(); - if (!_useAsyncIO) { // If we weren't opened for asynchronous I/O, we still call to the base implementation so that From 0a202c06a5164b8ef560b1eb5abb6bc1cd773c3e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:15:54 +0100 Subject: [PATCH 010/100] ReadAsync(Memory buffer, CancellationToken cancellationToken = default) --- .../src/System/IO/FileStream.cs | 36 ++----------------- .../src/System/IO/FileStreamImpl.cs | 17 --------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 738db4c88c00d..0b35f98ed7690 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -209,44 +209,12 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken return ValueTask.FromCanceled(cancellationToken); } - if (IsClosed) + if (_actualImplementation.IsClosed) { throw Error.GetFileNotOpen(); } - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Read is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's - // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream - // rather than something derived from it and what our BeginRead implementation is going to do. - return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)base.BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : - base.ReadAsync(buffer, cancellationToken); - } - - Task? t = ReadAsyncInternal(buffer, cancellationToken, out int synchronousResult); - return t != null ? - new ValueTask(t) : - new ValueTask(synchronousResult); - } - - private Task ReadAsyncTask(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - Task? t = ReadAsyncInternal(new Memory(buffer, offset, count), cancellationToken, out int synchronousResult); - - if (t == null) - { - t = _lastSynchronouslyCompletedTask; - Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully"); - - if (t == null || t.Result != synchronousResult) - { - _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult); - } - } - - return t; + return _actualImplementation.ReadAsync(buffer, cancellationToken); } public override void Write(byte[] buffer, int offset, int count) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 3fa43df47d06f..e790e0c9779d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -321,23 +321,6 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { - if (GetType() != typeof(FileStream)) - { - // If this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), - // which was introduced first, so delegate to the base which will delegate to that. - return base.ReadAsync(buffer, cancellationToken); - } - - if (cancellationToken.IsCancellationRequested) - { - return ValueTask.FromCanceled(cancellationToken); - } - - if (IsClosed) - { - throw Error.GetFileNotOpen(); - } - if (!_useAsyncIO) { // If we weren't opened for asynchronous I/O, we still call to the base implementation so that From c45e7ebb44a06752bc3d1d5aa421bf06e8107e41 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:17:11 +0100 Subject: [PATCH 011/100] Write(byte[] buffer, int offset, int count) --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 10 ++-------- .../src/System/IO/FileStreamImpl.cs | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 0b35f98ed7690..2e75203a71594 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -220,14 +220,8 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken public override void Write(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); - if (_useAsyncIO) - { - WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask().GetAwaiter().GetResult(); - } - else - { - WriteSpan(new ReadOnlySpan(buffer, offset, count)); - } + + _actualImplementation.Write(buffer, offset, count); } public override void Write(ReadOnlySpan buffer) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index e790e0c9779d1..827a3d7723fa2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -358,7 +358,6 @@ private Task ReadAsyncTask(byte[] buffer, int offset, int count, Cancellati public override void Write(byte[] buffer, int offset, int count) { - ValidateReadWriteArgs(buffer, offset, count); if (_useAsyncIO) { WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask().GetAwaiter().GetResult(); From cedc13ab6a3ab08b7d0b1705bb153dee71e5a342 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:19:49 +0100 Subject: [PATCH 012/100] Write(ReadOnlySpan buffer) --- .../src/System/IO/FileStream.cs | 7 ++++--- .../src/System/IO/FileStreamImpl.cs | 19 +------------------ 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 2e75203a71594..8b872734275d4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -226,13 +226,14 @@ public override void Write(byte[] buffer, int offset, int count) public override void Write(ReadOnlySpan buffer) { - if (GetType() == typeof(FileStream) && !_useAsyncIO) + if (GetType() == typeof(FileStream) && !_actualImplementation.IsAsync) { - if (_fileHandle.IsClosed) + if (_actualImplementation.IsClosed) { throw Error.GetFileNotOpen(); } - WriteSpan(buffer); + + _actualImplementation.Write(buffer); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 827a3d7723fa2..94998cc7a4fbd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -370,24 +370,7 @@ public override void Write(byte[] buffer, int offset, int count) public override void Write(ReadOnlySpan buffer) { - if (GetType() == typeof(FileStream) && !_useAsyncIO) - { - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - WriteSpan(buffer); - } - else - { - // This type is derived from FileStream and/or the stream is in async mode. If this is a - // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) - // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior - // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the - // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to - // Write(byte[],int,int), which will do the right thing if we're in async mode. - base.Write(buffer); - } + WriteSpan(buffer); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) From 4b3d04846a7ab71d94109b3396fe42062f2b53b5 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:20:54 +0100 Subject: [PATCH 013/100] WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) --- .../src/System/IO/FileStream.cs | 13 ++----------- .../src/System/IO/FileStreamImpl.cs | 17 ----------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 8b872734275d4..8d8dbb9f538a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -263,19 +263,10 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); - if (IsClosed) + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Write is invoked asynchronously. But we can do so using the base Stream's internal helper - // that bypasses delegating to BeginWrite, since we already know this is FileStream rather - // than something derived from it and what our BeginWrite implementation is going to do. - return (Task)base.BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); - } - - return WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + return _actualImplementation.WriteAsync(buffer, offset, count); } public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 94998cc7a4fbd..c4def329d0485 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -375,23 +375,6 @@ public override void Write(ReadOnlySpan buffer) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - ValidateBufferArguments(buffer, offset, count); - - if (GetType() != typeof(FileStream)) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. - return base.WriteAsync(buffer, offset, count, cancellationToken); - } - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - if (IsClosed) - throw Error.GetFileNotOpen(); - if (!_useAsyncIO) { // If we weren't opened for asynchronous I/O, we still call to the base implementation so that From bafc099ff6e8901b357554cdbcf5dab3dca7e8d4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:23:02 +0100 Subject: [PATCH 014/100] WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) --- .../src/System/IO/FileStream.cs | 15 ++------------- .../src/System/IO/FileStreamImpl.cs | 17 ----------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 8d8dbb9f538a2..510c346e661af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -283,23 +283,12 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return ValueTask.FromCanceled(cancellationToken); } - if (IsClosed) + if (_actualImplementation.IsClosed) { throw Error.GetFileNotOpen(); } - if (!_useAsyncIO) - { - // If we weren't opened for asynchronous I/O, we still call to the base implementation so that - // Write is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's - // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream - // rather than something derived from it and what our BeginWrite implementation is going to do. - return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : - base.WriteAsync(buffer, cancellationToken); - } - - return WriteAsyncInternal(buffer, cancellationToken); + return _actualImplementation.WriteAsync(buffer, cancellationToken); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index c4def329d0485..0592658e2c75c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -389,23 +389,6 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { - if (GetType() != typeof(FileStream)) - { - // If this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), - // which was introduced first, so delegate to the base which will delegate to that. - return base.WriteAsync(buffer, cancellationToken); - } - - if (cancellationToken.IsCancellationRequested) - { - return ValueTask.FromCanceled(cancellationToken); - } - - if (IsClosed) - { - throw Error.GetFileNotOpen(); - } - if (!_useAsyncIO) { // If we weren't opened for asynchronous I/O, we still call to the base implementation so that From 91513ed1140635e8258efdcd67a28822b47cfe13 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:27:06 +0100 Subject: [PATCH 015/100] Flush() and Flush(bool flushToDisk) --- .../src/System/IO/FileStream.cs | 15 +++------------ .../src/System/IO/FileStreamImpl.cs | 11 +---------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 510c346e661af..4bd6ec9c05386 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -294,11 +294,7 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo /// /// Clears buffers for this stream and causes any buffered data to be written to the file. /// - public override void Flush() - { - // Make sure that we call through the public virtual API - Flush(flushToDisk: false); - } + public override void Flush() => _actualImplementation.Flush(); /// /// Clears buffers for this stream, and if is true, @@ -306,14 +302,9 @@ public override void Flush() /// public virtual void Flush(bool flushToDisk) { - if (IsClosed) throw Error.GetFileNotOpen(); - - FlushInternalBuffer(); + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - if (flushToDisk && CanWrite) - { - FlushOSBuffer(); - } + _actualImplementation.Flush(flushToDisk); } /// Gets a value indicating whether the current stream supports reading. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 0592658e2c75c..44d3fa4b59a54 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -403,23 +403,14 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return WriteAsyncInternal(buffer, cancellationToken); } - /// - /// Clears buffers for this stream and causes any buffered data to be written to the file. - /// public override void Flush() { // Make sure that we call through the public virtual API Flush(flushToDisk: false); } - /// - /// Clears buffers for this stream, and if is true, - /// causes any buffered data to be written to the file. - /// - public virtual void Flush(bool flushToDisk) + internal override void Flush(bool flushToDisk) { - if (IsClosed) throw Error.GetFileNotOpen(); - FlushInternalBuffer(); if (flushToDisk && CanWrite) From ecd62f0788cf2bbe67ffab82f553b5ef58779f8b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:28:13 +0100 Subject: [PATCH 016/100] CanRead and CanWrite --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 4 ++-- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 4bd6ec9c05386..e6ba8ac5d1b09 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -308,10 +308,10 @@ public virtual void Flush(bool flushToDisk) } /// Gets a value indicating whether the current stream supports reading. - public override bool CanRead => !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; + public override bool CanRead => _actualImplementation.CanRead; /// Gets a value indicating whether the current stream supports writing. - public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; + public override bool CanWrite => _actualImplementation.CanWrite; /// Validates arguments to Read and Write and throws resulting exceptions. /// The buffer to read from or write to. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 44d3fa4b59a54..c1314cb564b8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -419,10 +419,8 @@ internal override void Flush(bool flushToDisk) } } - /// Gets a value indicating whether the current stream supports reading. public override bool CanRead => !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; - /// Gets a value indicating whether the current stream supports writing. public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; /// Validates arguments to Read and Write and throws resulting exceptions. From 1faf34a0dddfde99f4590f11ff5972b8f3b71552 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:36:07 +0100 Subject: [PATCH 017/100] input validation should be always performed in the public type --- .../src/System/IO/FileStream.cs | 9 +++++++-- .../src/System/IO/FileStreamImpl.cs | 12 ------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index e6ba8ac5d1b09..c267185e2f26e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -153,7 +153,12 @@ public override Task FlushAsync(CancellationToken cancellationToken) return _actualImplementation.FlushAsync(cancellationToken); } - public override int Read(byte[] buffer, int offset, int count) => _actualImplementation.Read(buffer, offset, count); + public override int Read(byte[] buffer, int offset, int count) + { + ValidateReadWriteArgs(buffer, offset, count); + + return _actualImplementation.Read(buffer, offset, count); + } public override int Read(Span buffer) { @@ -320,7 +325,7 @@ public virtual void Flush(bool flushToDisk) private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); - if (_fileHandle.IsClosed) + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index c1314cb564b8c..ddd0fe165a372 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -288,7 +288,6 @@ public override Task FlushAsync(CancellationToken cancellationToken) public override int Read(byte[] buffer, int offset, int count) { - ValidateReadWriteArgs(buffer, offset, count); return _useAsyncIO ? ReadAsyncTask(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult() : ReadSpan(new Span(buffer, offset, count)); @@ -423,17 +422,6 @@ internal override void Flush(bool flushToDisk) public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; - /// Validates arguments to Read and Write and throws resulting exceptions. - /// The buffer to read from or write to. - /// The zero-based offset into the buffer. - /// The maximum number of bytes to read or write. - private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) - { - ValidateBufferArguments(buffer, offset, count); - if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); - } - /// Sets the length of this stream to the given value. /// The new length of the stream. public override void SetLength(long value) From fe308fe98ee80052a945248d9606872ae2783dee Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:36:34 +0100 Subject: [PATCH 018/100] SetLength(long value) --- .../src/System/IO/FileStream.cs | 8 ++++---- .../src/System/IO/FileStreamImpl.cs | 16 +--------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index c267185e2f26e..845682bf57f2c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -335,14 +335,14 @@ public override void SetLength(long value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - if (_fileHandle.IsClosed) + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - if (!CanSeek) + if (!_actualImplementation.CanSeek) throw Error.GetSeekNotSupported(); - if (!CanWrite) + if (!_actualImplementation.CanWrite) throw Error.GetWriteNotSupported(); - SetLengthInternal(value); + _actualImplementation.SetLength(value); } public virtual SafeFileHandle SafeFileHandle diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index ddd0fe165a372..204d442636cb6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -422,21 +422,7 @@ internal override void Flush(bool flushToDisk) public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; - /// Sets the length of this stream to the given value. - /// The new length of the stream. - public override void SetLength(long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); - if (!CanSeek) - throw Error.GetSeekNotSupported(); - if (!CanWrite) - throw Error.GetWriteNotSupported(); - - SetLengthInternal(value); - } + public override void SetLength(long value) => SetLengthInternal(value); public virtual SafeFileHandle SafeFileHandle { From 082d63058056b7bbe9238c10d9ef7e100730ec47 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:40:13 +0100 Subject: [PATCH 019/100] SafeFileHandle, Name, IsAsync and Length --- .../src/System/IO/FileStream.cs | 20 ++++++------------- .../src/System/IO/FileStreamImpl.cs | 19 ++++-------------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 845682bf57f2c..9732f12619c51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -345,30 +345,22 @@ public override void SetLength(long value) _actualImplementation.SetLength(value); } - public virtual SafeFileHandle SafeFileHandle - { - get - { - Flush(); - _exposedHandle = true; - return _fileHandle; - } - } + public virtual SafeFileHandle SafeFileHandle => _actualImplementation.SafeFileHandle; /// Gets the path that was passed to the constructor. - public virtual string Name => _path ?? SR.IO_UnknownFileName; + public virtual string Name => _actualImplementation.Name; /// Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously. - public virtual bool IsAsync => _useAsyncIO; + public virtual bool IsAsync => _actualImplementation.IsAsync; /// Gets the length of the stream in bytes. public override long Length { get { - if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); - if (!CanSeek) throw Error.GetSeekNotSupported(); - return GetLengthInternal(); + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); + if (!_actualImplementation.CanSeek) throw Error.GetSeekNotSupported(); + return _actualImplementation.Length; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 204d442636cb6..aab662179e2cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -424,7 +424,7 @@ internal override void Flush(bool flushToDisk) public override void SetLength(long value) => SetLengthInternal(value); - public virtual SafeFileHandle SafeFileHandle + internal override SafeFileHandle SafeFileHandle { get { @@ -434,22 +434,11 @@ public virtual SafeFileHandle SafeFileHandle } } - /// Gets the path that was passed to the constructor. - public virtual string Name => _path ?? SR.IO_UnknownFileName; + internal override string Name => _path ?? SR.IO_UnknownFileName; - /// Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously. - public virtual bool IsAsync => _useAsyncIO; + internal override bool IsAsync => _useAsyncIO; - /// Gets the length of the stream in bytes. - public override long Length - { - get - { - if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); - if (!CanSeek) throw Error.GetSeekNotSupported(); - return GetLengthInternal(); - } - } + public override long Length => GetLengthInternal(); /// /// Verify that the actual position of the OS's handle equals what we expect it to. From cb6adc37eede6069f92cba076a3b485b7f210f68 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:42:47 +0100 Subject: [PATCH 020/100] Position --- .../src/System/IO/FileStream.cs | 62 ++----------------- .../src/System/IO/FileStreamImpl.cs | 9 --- 2 files changed, 4 insertions(+), 67 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 9732f12619c51..a0c5537457ba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -364,50 +364,6 @@ public override long Length } } - /// - /// Verify that the actual position of the OS's handle equals what we expect it to. - /// This will fail if someone else moved the UnixFileStream's handle or if - /// our position updating code is incorrect. - /// - private void VerifyOSHandlePosition() - { - bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it -#if DEBUG - verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be -#endif - if (verifyPosition && CanSeek) - { - long oldPos = _filePosition; // SeekCore will override the current _position, so save it now - long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); - if (oldPos != curPos) - { - // For reads, this is non-fatal but we still could have returned corrupted - // data in some cases, so discard the internal buffer. For writes, - // this is a problem; discard the buffer and error out. - _readPos = _readLength = 0; - if (_writePos > 0) - { - _writePos = 0; - throw new IOException(SR.IO_FileStreamHandlePosition); - } - } - } - } - - /// Verifies that state relating to the read/write buffer is consistent. - [Conditional("DEBUG")] - private void AssertBufferInvariants() - { - // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength - Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength); - - // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength - Debug.Assert(0 <= _writePos && _writePos <= _bufferLength); - - // Read buffering and write buffering can't both be active - Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0); - } - /// Validates that we're ready to read from the stream. private void PrepareForReading() { @@ -424,30 +380,20 @@ public override long Position { get { - if (_fileHandle.IsClosed) + if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - if (!CanSeek) + if (!_actualImplementation.CanSeek) throw Error.GetSeekNotSupported(); - AssertBufferInvariants(); - VerifyOSHandlePosition(); - - // We may have read data into our buffer from the handle, such that the handle position - // is artificially further along than the consumer's view of the stream's position. - // Thus, when reading, our position is really starting from the handle position negatively - // offset by the number of bytes in the buffer and positively offset by the number of - // bytes into that buffer we've read. When writing, both the read length and position - // must be zero, and our position is just the handle position offset positive by how many - // bytes we've written into the buffer. - return (_filePosition - _readLength) + _readPos + _writePos; + return _actualImplementation.Positon; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - Seek(value, SeekOrigin.Begin); + _actualImplementation.Seek(value, SeekOrigin.Begin); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index aab662179e2cd..567d7d9a7dad6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -500,12 +500,6 @@ public override long Position { get { - if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); - - if (!CanSeek) - throw Error.GetSeekNotSupported(); - AssertBufferInvariants(); VerifyOSHandlePosition(); @@ -520,9 +514,6 @@ public override long Position } set { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - Seek(value, SeekOrigin.Begin); } } From 2feeefa2191b2a38c87ad7dbeddaa2208d253bc6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:43:28 +0100 Subject: [PATCH 021/100] IsClosed --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index a0c5537457ba2..540e2bc441c27 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -397,7 +397,7 @@ public override long Position } } - internal virtual bool IsClosed => _fileHandle.IsClosed; + internal virtual bool IsClosed => _actualImplementation.IsClosed; private static bool IsIoRelatedException(Exception e) => // These all derive from IOException diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 567d7d9a7dad6..d9e975691d4f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -518,7 +518,7 @@ public override long Position } } - internal virtual bool IsClosed => _fileHandle.IsClosed; + internal override bool IsClosed => _fileHandle.IsClosed; private static bool IsIoRelatedException(Exception e) => // These all derive from IOException From 130131853a83331579f3433ff3dc5ba653531edd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 13:56:06 +0100 Subject: [PATCH 022/100] ReadByte() --- .../src/System/IO/FileStream.cs | 49 +------------------ 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 540e2bc441c27..5d2f2a2a664ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -364,17 +364,6 @@ public override long Length } } - /// Validates that we're ready to read from the stream. - private void PrepareForReading() - { - if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); - if (_readLength == 0 && !CanRead) - throw Error.GetReadNotSupported(); - - AssertBufferInvariants(); - } - /// Gets or sets the position within the current stream public override long Position { @@ -415,25 +404,6 @@ e is UnauthorizedAccessException || e is NotSupportedException || (e is ArgumentException && !(e is ArgumentNullException)); - /// - /// Gets the array used for buffering reading and writing. - /// If the array hasn't been allocated, this will lazily allocate it. - /// - /// The buffer. - private byte[] GetBuffer() - { - Debug.Assert(_buffer == null || _buffer.Length == _bufferLength); - if (_buffer == null) - { - _buffer = new byte[_bufferLength]; - OnBufferAllocated(); - } - - return _buffer; - } - - partial void OnBufferAllocated(); - /// /// Flushes the internal read/write buffer for this stream. If write data has been buffered, /// that data is written out to the underlying file. Or if data has been buffered for @@ -477,24 +447,7 @@ private void FlushReadBuffer() /// Reads a byte from the file stream. Returns the byte cast to an int /// or -1 if reading from the end of the stream. /// - public override int ReadByte() - { - PrepareForReading(); - - byte[] buffer = GetBuffer(); - if (_readPos == _readLength) - { - FlushWriteBuffer(); - _readLength = FillReadBufferForReadByte(); - _readPos = 0; - if (_readLength == 0) - { - return -1; - } - } - - return buffer[_readPos++]; - } + public override int ReadByte() => _actualImplementation.ReadByte(); /// /// Writes a byte to the current position in the stream and advances the position From 7f15975c814e4c14963b958e8d85bcc2f785d1ea Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:05:51 +0100 Subject: [PATCH 023/100] WriteByte(byte value) --- .../src/System/IO/FileStream.cs | 71 +------------------ 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 5d2f2a2a664ae..9128f2da3ad72 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -404,45 +404,6 @@ e is UnauthorizedAccessException || e is NotSupportedException || (e is ArgumentException && !(e is ArgumentNullException)); - /// - /// Flushes the internal read/write buffer for this stream. If write data has been buffered, - /// that data is written out to the underlying file. Or if data has been buffered for - /// reading from the stream, the data is dumped and our position in the underlying file - /// is rewound as necessary. This does not flush the OS buffer. - /// - private void FlushInternalBuffer() - { - AssertBufferInvariants(); - if (_writePos > 0) - { - FlushWriteBuffer(); - } - else if (_readPos < _readLength && CanSeek) - { - FlushReadBuffer(); - } - } - - /// Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary. - private void FlushReadBuffer() - { - // Reading is done by blocks from the file, but someone could read - // 1 byte from the buffer then write. At that point, the OS's file - // pointer is out of sync with the stream's position. All write - // functions should call this function to preserve the position in the file. - - AssertBufferInvariants(); - Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!"); - - int rewind = _readPos - _readLength; - if (rewind != 0) - { - Debug.Assert(CanSeek, "FileStream will lose buffered read data now."); - SeekCore(_fileHandle, rewind, SeekOrigin.Current); - } - _readPos = _readLength = 0; - } - /// /// Reads a byte from the file stream. Returns the byte cast to an int /// or -1 if reading from the end of the stream. @@ -454,37 +415,7 @@ private void FlushReadBuffer() /// within the stream by one byte. /// /// The byte to write to the stream. - public override void WriteByte(byte value) - { - PrepareForWriting(); - - // Flush the write buffer if it's full - if (_writePos == _bufferLength) - FlushWriteBufferForWriteByte(); - - // We now have space in the buffer. Store the byte. - GetBuffer()[_writePos++] = value; - } - - /// - /// Validates that we're ready to write to the stream, - /// including flushing a read buffer if necessary. - /// - private void PrepareForWriting() - { - if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); - - // Make sure we're good to write. We only need to do this if there's nothing already - // in our write buffer, since if there is something in the buffer, we've already done - // this checking and flushing. - if (_writePos == 0) - { - if (!CanWrite) throw Error.GetWriteNotSupported(); - FlushReadBuffer(); - Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); - } - } + public override void WriteByte(byte value) => _actualImplementation.WriteByte(value); ~FileStream() { From 6089931c3b6d684268a006225394ef426ce126e9 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:07:40 +0100 Subject: [PATCH 024/100] finalizer --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 9128f2da3ad72..73c16556c958d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -422,7 +422,7 @@ e is NotSupportedException || // Preserved for compatibility since FileStream has defined a // finalizer in past releases and derived classes may depend // on Dispose(false) call. - Dispose(false); + _actualImplementation.Dispose(false); } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index d9e975691d4f8..f075d71ac1152 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -654,14 +654,6 @@ private void PrepareForWriting() } } - ~FileStream() - { - // Preserved for compatibility since FileStream has defined a - // finalizer in past releases and derived classes may depend - // on Dispose(false) call. - Dispose(false); - } - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); From d26deb440494b0ba172a6b7c1aa2f82873b841cd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:09:45 +0100 Subject: [PATCH 025/100] BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 8 ++++---- .../src/System/IO/FileStreamImpl.cs | 9 +-------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 73c16556c958d..1f0db5488b951 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -428,13 +428,13 @@ e is NotSupportedException || public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + if (_actualImplementation.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!_actualImplementation.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - if (!IsAsync) + if (!_actualImplementation.IsAsync) return base.BeginRead(buffer, offset, count, callback, state); else - return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); + return _actualImplementation.BeginRead(buffer, offset, count, callback, state); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index f075d71ac1152..80028d7db983c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -656,14 +656,7 @@ private void PrepareForWriting() public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { - ValidateBufferArguments(buffer, offset, count); - if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - - if (!IsAsync) - return base.BeginRead(buffer, offset, count, callback, state); - else - return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); + return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) From 0a9fd33e8ec35c1a00a7c8ee9d05cd4646d5194a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:10:59 +0100 Subject: [PATCH 026/100] BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 8 ++++---- .../src/System/IO/FileStreamImpl.cs | 9 +-------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 1f0db5488b951..9928054c50af0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -440,13 +440,13 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + if (_actualImplementation.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!_actualImplementation.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - if (!IsAsync) + if (!_actualImplementation.IsAsync) return base.BeginWrite(buffer, offset, count, callback, state); else - return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); + return _actualImplementation.BeginWrite(buffer, offset, count, callback, state); } public override int EndRead(IAsyncResult asyncResult) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 80028d7db983c..d160685d5825e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -661,14 +661,7 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { - ValidateBufferArguments(buffer, offset, count); - if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - - if (!IsAsync) - return base.BeginWrite(buffer, offset, count, callback, state); - else - return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); + return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); } public override int EndRead(IAsyncResult asyncResult) From f681b6f2eecdc0ac6572e73c877ed2a4338c573e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:12:29 +0100 Subject: [PATCH 027/100] EndRead and EndWrite --- .../src/System/IO/FileStream.cs | 4 ++-- .../src/System/IO/FileStreamImpl.cs | 22 ------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 9928054c50af0..375b75312366c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -454,7 +454,7 @@ public override int EndRead(IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - if (!IsAsync) + if (!_actualImplementation.IsAsync) return base.EndRead(asyncResult); else return TaskToApm.End(asyncResult); @@ -465,7 +465,7 @@ public override void EndWrite(IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - if (!IsAsync) + if (!_actualImplementation.IsAsync) base.EndWrite(asyncResult); else TaskToApm.End(asyncResult); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index d160685d5825e..5787c10b4ed08 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -663,27 +663,5 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As { return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); } - - public override int EndRead(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException(nameof(asyncResult)); - - if (!IsAsync) - return base.EndRead(asyncResult); - else - return TaskToApm.End(asyncResult); - } - - public override void EndWrite(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException(nameof(asyncResult)); - - if (!IsAsync) - base.EndWrite(asyncResult); - else - TaskToApm.End(asyncResult); - } } } From f8803352a608402553f83fb1e797a41f0997209e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:24:08 +0100 Subject: [PATCH 028/100] FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 375b75312366c..e087a14466818 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -100,8 +100,8 @@ public FileStream(SafeFileHandle handle, FileAccess access) } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, GetDefaultIsAsync(handle)) { + _actualImplementation = new FileStreamImpl(handle, access, bufferSize); } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 5787c10b4ed08..84839894933a9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -138,6 +138,11 @@ private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, InitFromHandle(handle, access, isAsync); } + internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize) + : this(handle, access, bufferSize, GetDefaultIsAsync(handle)) + { + } + internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); From fceeb0dd463c0c6478b9302dc582ad7343ac02a9 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:24:43 +0100 Subject: [PATCH 029/100] fix compilation errors --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index e087a14466818..65db51c507a72 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -12,7 +12,7 @@ namespace System.IO { public partial class FileStream : Stream { - private readonly LockableStream _actualImplementation; + private readonly FileStreamImplBase _actualImplementation; private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; @@ -271,7 +271,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - return _actualImplementation.WriteAsync(buffer, offset, count); + return _actualImplementation.WriteAsync(buffer, offset, count, cancellationToken); } public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) @@ -375,7 +375,7 @@ public override long Position if (!_actualImplementation.CanSeek) throw Error.GetSeekNotSupported(); - return _actualImplementation.Positon; + return _actualImplementation.Position; } set { @@ -422,7 +422,7 @@ e is NotSupportedException || // Preserved for compatibility since FileStream has defined a // finalizer in past releases and derived classes may depend // on Dispose(false) call. - _actualImplementation.Dispose(false); + Dispose(false); } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) From 80fdf688dbcfc75b6e2c6c1f04d682c50cf2601b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:25:56 +0100 Subject: [PATCH 030/100] remove unused fields and methods --- .../src/System/IO/FileStream.cs | 71 ------------------- .../src/System/IO/FileStreamImpl.cs | 2 - 2 files changed, 73 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 65db51c507a72..2a7c4df2b512a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -18,58 +15,6 @@ public partial class FileStream : Stream private const bool DefaultIsAsync = false; internal const int DefaultBufferSize = 4096; - private byte[]? _buffer; - private int _bufferLength; - private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws - - /// Whether the file is opened for reading, writing, or both. - private readonly FileAccess _access; - - /// The path to the opened file. - private readonly string? _path; - - /// The next available byte to be read from the _buffer. - private int _readPos; - - /// The number of valid bytes in _buffer. - private int _readLength; - - /// The next location in which a write should occur to the buffer. - private int _writePos; - - /// - /// Whether asynchronous read/write/flush operations should be performed using async I/O. - /// On Windows FileOptions.Asynchronous controls how the file handle is configured, - /// and then as a result how operations are issued against that file handle. On Unix, - /// there isn't any distinction around how file descriptors are created for async vs - /// sync, but we still differentiate how the operations are issued in order to provide - /// similar behavioral semantics and performance characteristics as on Windows. On - /// Windows, if non-async, async read/write requests just delegate to the base stream, - /// and no attempt is made to synchronize between sync and async operations on the stream; - /// if async, then async read/write requests are implemented specially, and sync read/write - /// requests are coordinated with async ones by implementing the sync ones over the async - /// ones. On Unix, we do something similar. If non-async, async read/write requests just - /// delegate to the base stream, and no attempt is made to synchronize. If async, we use - /// a semaphore to coordinate both sync and async operations. - /// - private readonly bool _useAsyncIO; - - /// cached task for read ops that complete synchronously - private Task? _lastSynchronouslyCompletedTask; - - /// - /// Currently cached position in the stream. This should always mirror the underlying file's actual position, - /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which - /// point we attempt to error out. - /// - private long _filePosition; - - /// Whether the file stream's handle has been exposed. - private bool _exposedHandle; - - /// Caches whether Serialization Guard has been disabled for file writes - private static int s_cachedSerializationSwitch; - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access) : this(handle, access, true, DefaultBufferSize, false) @@ -388,22 +333,6 @@ public override long Position internal virtual bool IsClosed => _actualImplementation.IsClosed; - private static bool IsIoRelatedException(Exception e) => - // These all derive from IOException - // DirectoryNotFoundException - // DriveNotFoundException - // EndOfStreamException - // FileLoadException - // FileNotFoundException - // PathTooLongException - // PipeException - e is IOException || - // Note that SecurityException is only thrown on runtimes that support CAS - // e is SecurityException || - e is UnauthorizedAccessException || - e is NotSupportedException || - (e is ArgumentException && !(e is ArgumentNullException)); - /// /// Reads a byte from the file stream. Returns the byte cast to an int /// or -1 if reading from the end of the stream. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 84839894933a9..c7c17365596cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -31,9 +31,7 @@ internal abstract class FileStreamImplBase : Stream internal sealed partial class FileStreamImpl : FileStreamImplBase { - private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; - internal const int DefaultBufferSize = 4096; private byte[]? _buffer; private int _bufferLength; From e1459f11133c1b8be02c8d65bec83a8b9006053e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:27:00 +0100 Subject: [PATCH 031/100] CanSeek and Seek --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 2a7c4df2b512a..2732b1b4486f0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -399,5 +399,9 @@ public override void EndWrite(IAsyncResult asyncResult) else TaskToApm.End(asyncResult); } + + public override bool CanSeek => _actualImplementation.CanSeek; + + public override long Seek(long offset, SeekOrigin origin) => _actualImplementation.Seek(offset, origin); } } From 7cf008d951a1c4793bfd3003c09ceec173db99df Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 14:29:50 +0100 Subject: [PATCH 032/100] fix all compilation errors --- .../System/IO/FileStreamCompletionSource.Win32.cs | 12 ++++++------ .../src/System/IO/FileStreamImpl.Windows.cs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index ccd0f1ec9a2c2..ec8c657ff5c65 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -25,7 +25,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource private static Action? s_cancelCallback; - private readonly FileStream _stream; + private readonly FileStreamImpl _stream; private readonly int _numBufferedBytes; private CancellationTokenRegistration _cancellationRegistration; #if DEBUG @@ -35,7 +35,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource private long _result; // Using long since this needs to be used in Interlocked APIs // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - protected FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[]? bytes) + protected FileStreamCompletionSource(FileStreamImpl stream, int numBufferedBytes, byte[]? bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) { _numBufferedBytes = numBufferedBytes; @@ -132,8 +132,8 @@ internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated // overlapped was already in use by another operation). object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); - Debug.Assert(state is FileStream || state is FileStreamCompletionSource); - FileStreamCompletionSource completionSource = state is FileStream fs ? + Debug.Assert(state is FileStreamImpl || state is FileStreamCompletionSource); + FileStreamCompletionSource completionSource = state is FileStreamImpl fs ? fs._currentOverlappedOwner! : // must be owned (FileStreamCompletionSource)state!; Debug.Assert(completionSource != null); @@ -220,7 +220,7 @@ private static void Cancel(object? state) } } - public static FileStreamCompletionSource Create(FileStream stream, int numBufferedBytesRead, ReadOnlyMemory memory) + public static FileStreamCompletionSource Create(FileStreamImpl stream, int numBufferedBytesRead, ReadOnlyMemory memory) { // If the memory passed in is the stream's internal buffer, we can use the base FileStreamCompletionSource, // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived @@ -241,7 +241,7 @@ private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSour { private MemoryHandle _handle; // mutable struct; do not make this readonly - internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory memory) : + internal MemoryFileStreamCompletionSource(FileStreamImpl stream, int numBufferedBytes, ReadOnlyMemory memory) : base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes { _handle = memory.Pin(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index 9c7261329642b..a58e1c287a2fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -274,7 +274,7 @@ private async ValueTask DisposeAsyncCore() { if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0) { - await FlushAsyncInternal(default).ConfigureAwait(false); + await FlushAsync(default).ConfigureAwait(false); } } finally @@ -1474,7 +1474,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion internal static readonly IOCompletionCallback s_callback = IOCallback; /// The FileStream that owns this instance. - internal readonly FileStream _fileStream; + internal readonly FileStreamImpl _fileStream; /// Tracked position representing the next location from which to read. internal long _position; @@ -1495,7 +1495,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion internal object CancellationLock => this; /// Initialize the awaitable. - internal AsyncCopyToAwaitable(FileStream fileStream) + internal AsyncCopyToAwaitable(FileStreamImpl fileStream) { _fileStream = fileStream; } From d1d6528352396d336ef5848d4dfb92d855dc5e91 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 15:07:55 +0100 Subject: [PATCH 033/100] add missing overrides (implemented in OS-specific files and not caught previously) all tests on Windows are passing --- .../src/System/IO/FileStream.cs | 14 ++++++++++++++ .../src/System/IO/FileStreamImpl.Windows.cs | 9 ++------- .../src/System/IO/FileStreamImpl.cs | 4 ++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 2732b1b4486f0..41b13ce1bb5df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -354,6 +354,20 @@ public override long Position Dispose(false); } + protected override void Dispose(bool disposing) => _actualImplementation.DisposeInternal(disposing); + + public override ValueTask DisposeAsync() + { + if (OperatingSystem.IsWindows() && GetType() != typeof(FileStream)) + { + return base.DisposeAsync(); + } + + return _actualImplementation.DisposeAsync(); + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _actualImplementation.CopyToAsync(destination, bufferSize, cancellationToken); + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index a58e1c287a2fa..429e506116e3c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -261,12 +261,7 @@ protected override void Dispose(bool disposing) } } - public override ValueTask DisposeAsync() => - GetType() == typeof(FileStream) ? - DisposeAsyncCore() : - base.DisposeAsync(); - - private async ValueTask DisposeAsyncCore() + public override async ValueTask DisposeAsync() { // Same logic as in Dispose(), except with async counterparts. // TODO: https://github.com/dotnet/runtime/issues/27643: FlushAsync does synchronous work. @@ -1247,7 +1242,7 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio // typical read/write looping. We also need to take this path if this is a derived // instance from FileStream, as a derived type could have overridden ReadAsync, in which // case our custom CopyToAsync implementation isn't necessarily correct. - if (!_useAsyncIO || GetType() != typeof(FileStream)) + if (!_useAsyncIO) { return base.CopyToAsync(destination, bufferSize, cancellationToken); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index c7c17365596cf..83be6117775ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -27,6 +27,8 @@ internal abstract class FileStreamImplBase : Stream internal abstract void Unlock(long position, long length); internal abstract void Flush(bool flushToDisk); + + internal abstract void DisposeInternal(bool disposing); } internal sealed partial class FileStreamImpl : FileStreamImplBase @@ -226,6 +228,8 @@ internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare } } + internal override void DisposeInternal(bool disposing) => Dispose(disposing); + internal override IntPtr Handle => SafeFileHandle.DangerousGetHandle(); internal override void Lock(long position, long length) From d30f3718aa1f3081de4074e5f419edf485a9c8d2 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 15:40:21 +0100 Subject: [PATCH 034/100] make sure FileStream is not used in FileStreamImpl --- .../src/System/IO/FileStreamImpl.Unix.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index bdf35825743a2..0e80c6c44d496 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -305,7 +305,7 @@ public override ValueTask DisposeAsync() // override may already exist on a derived type. if (_useAsyncIO && _writePos > 0) { - return new ValueTask(Task.Factory.StartNew(static s => ((FileStream)s!).Dispose(), this, + return new ValueTask(Task.Factory.StartNew(static s => ((FileStreamImpl)s!).Dispose(), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); } @@ -530,7 +530,7 @@ private unsafe int ReadNative(Span buffer) // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStream)s!; + var thisRef = (FileStreamImpl)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -691,7 +691,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStream)s!; + var thisRef = (FileStreamImpl)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -775,7 +775,7 @@ public override long Seek(long offset, SeekOrigin origin) /// The new position in the stream. private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin) { - Debug.Assert(!fileHandle.IsClosed && (GetType() != typeof(FileStream) || CanSeekCore(fileHandle))); // verify that we can seek, but only if CanSeek won't be a virtual call (which could happen in the ctor) + Debug.Assert(!fileHandle.IsClosed && CanSeekCore(fileHandle)); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); long pos = CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin)); // SeekOrigin values are the same as Interop.libc.SeekWhence values From 99868c4d092ff580f144d3d74ac097979ee220b9 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 17:53:52 +0100 Subject: [PATCH 035/100] rename FileStreamImplBase to FileStreamStrategy and move it to a separate file --- .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/IO/FileStream.cs | 2 +- .../IO/FileStreamCompletionSource.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.OSX.cs | 2 +- .../src/System/IO/FileStreamImpl.Lock.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.Windows.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 23 +-------------- .../src/System/IO/FileStreamStrategy.cs | 28 +++++++++++++++++++ 10 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 79639fa934768..765fe70f2aa99 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -395,6 +395,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 41b13ce1bb5df..70f28a6b18118 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -9,7 +9,7 @@ namespace System.IO { public partial class FileStream : Stream { - private readonly FileStreamImplBase _actualImplementation; + private readonly FileStreamStrategy _actualImplementation; private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index ec8c657ff5c65..7e7a07a44255e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs index 7cfbb71cd32ce..7330a5147d988 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { private static void LockInternal(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs index 379dc679f62cd..ab4fc563020b3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index 0e80c6c44d496..6ec6179e9756c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { /// File mode. private FileMode _mode; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs index 48c1c5f992d31..cf1b4d117c6c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs @@ -6,7 +6,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index 429e506116e3c..ea6ff7a138e69 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -39,7 +39,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 83be6117775ce..5a0be6c85b9ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -10,28 +10,7 @@ namespace System.IO { - internal abstract class FileStreamImplBase : Stream - { - internal abstract bool IsAsync { get; } - - internal abstract string Name { get; } - - internal abstract IntPtr Handle { get; } - - internal abstract SafeFileHandle SafeFileHandle { get; } - - internal abstract bool IsClosed { get; } - - internal abstract void Lock(long position, long length); - - internal abstract void Unlock(long position, long length); - - internal abstract void Flush(bool flushToDisk); - - internal abstract void DisposeInternal(bool disposing); - } - - internal sealed partial class FileStreamImpl : FileStreamImplBase + internal sealed partial class FileStreamImpl : FileStreamStrategy { private const bool DefaultIsAsync = false; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs new file mode 100644 index 0000000000000..f0e5df36c701f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs @@ -0,0 +1,28 @@ +ï»ż// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; + +namespace System.IO +{ + internal abstract class FileStreamStrategy : Stream + { + internal abstract bool IsAsync { get; } + + internal abstract string Name { get; } + + internal abstract IntPtr Handle { get; } + + internal abstract SafeFileHandle SafeFileHandle { get; } + + internal abstract bool IsClosed { get; } + + internal abstract void Lock(long position, long length); + + internal abstract void Unlock(long position, long length); + + internal abstract void Flush(bool flushToDisk); + + internal abstract void DisposeInternal(bool disposing); + } +} From 07ed8506cf09b199bb550c69ab8e243f43762797 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 18:21:51 +0100 Subject: [PATCH 036/100] it might seem to have to sense now, but we plan to introduce dedicated strategies for sync and async implementation --- .../src/System/IO/FileStream.cs | 32 ++++++++++++++++--- .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Win32.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 5 --- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 70f28a6b18118..27502370a9326 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -36,7 +36,16 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { - _actualImplementation = new FileStreamImpl(handle, access, ownsHandle, bufferSize, isAsync); + // it might seem to have to sense now, but we plan to introduce dedicated strategies for sync and async implementation + switch (isAsync) + { + case true: + _actualImplementation = new FileStreamImpl(handle, access, ownsHandle, bufferSize, true); + return; + case false: + _actualImplementation = new FileStreamImpl(handle, access, ownsHandle, bufferSize, false); + return; + } } public FileStream(SafeFileHandle handle, FileAccess access) @@ -45,13 +54,21 @@ public FileStream(SafeFileHandle handle, FileAccess access) } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) + : this(handle, access, bufferSize, FileStreamImpl.GetDefaultIsAsync(handle)) { - _actualImplementation = new FileStreamImpl(handle, access, bufferSize); } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - _actualImplementation = new FileStreamImpl(handle, access, bufferSize, isAsync); + switch (isAsync) + { + case true: + _actualImplementation = new FileStreamImpl(handle, access, bufferSize, true); + return; + case false: + _actualImplementation = new FileStreamImpl(handle, access, bufferSize, false); + return; + } } public FileStream(string path, FileMode mode) : @@ -76,7 +93,14 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { - _actualImplementation = new FileStreamImpl(path, mode, access, share, bufferSize, options); + if ((options & FileOptions.Asynchronous) != 0) + { + _actualImplementation = new FileStreamImpl(path, mode, access, share, bufferSize, options); + } + else + { + _actualImplementation = new FileStreamImpl(path, mode, access, share, bufferSize, options); + } } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index 6ec6179e9756c..ea3e21bd2c7b1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -61,7 +61,7 @@ private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions op return SafeFileHandle.Open(_path!, openFlags, (int)OpenPermissions); } - private static bool GetDefaultIsAsync(SafeFileHandle handle) => handle.IsAsync ?? DefaultIsAsync; + internal static bool GetDefaultIsAsync(SafeFileHandle handle) => handle.IsAsync ?? DefaultIsAsync; /// Initializes a stream for reading or writing a Unix file. /// How the file should be opened. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs index cf1b4d117c6c6..c2f43b7c81ff2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs @@ -45,7 +45,7 @@ private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare shar } } - private static bool GetDefaultIsAsync(SafeFileHandle handle) + internal static bool GetDefaultIsAsync(SafeFileHandle handle) { return handle.IsAsync ?? !IsHandleSynchronous(handle, ignoreInvalid: true) ?? DefaultIsAsync; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 5a0be6c85b9ca..7e13b3043ffe4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -117,11 +117,6 @@ private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, InitFromHandle(handle, access, isAsync); } - internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, GetDefaultIsAsync(handle)) - { - } - internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); From 19ef1a75010b11d0923920388b62d74041dd5883 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 15 Jan 2021 18:32:08 +0100 Subject: [PATCH 037/100] some minor polishing --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 27502370a9326..785e4b29fd87a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -7,13 +7,13 @@ namespace System.IO { - public partial class FileStream : Stream + public class FileStream : Stream { - private readonly FileStreamStrategy _actualImplementation; - + internal const int DefaultBufferSize = 4096; private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; - internal const int DefaultBufferSize = 4096; + + private readonly FileStreamStrategy _actualImplementation; [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access) @@ -36,7 +36,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { - // it might seem to have to sense now, but we plan to introduce dedicated strategies for sync and async implementation + // it might seem to have to sense now, but we plan to introduce dedicated strategies for sync and async implementations switch (isAsync) { case true: From 50a6850633a1f69c0cef5f0f92c3b9a499e4cf9a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 12:24:34 +0100 Subject: [PATCH 038/100] reference field can be null in finalizer --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 785e4b29fd87a..1375ed707a4e7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -378,7 +378,13 @@ public override long Position Dispose(false); } - protected override void Dispose(bool disposing) => _actualImplementation.DisposeInternal(disposing); + protected override void Dispose(bool disposing) + { + if (_actualImplementation != null) // possible in finalizer + { + _actualImplementation.DisposeInternal(disposing); + } + } public override ValueTask DisposeAsync() { From a0b3196117442fc0a74f220120b6a365cf71cafe Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 12:26:35 +0100 Subject: [PATCH 039/100] it looks like having this finalizer is mandatory, as we can not guarantee that the Strategy won't be null in FileStream finalizer --- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 7e13b3043ffe4..ec22969c5d460 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -202,6 +202,13 @@ internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare } } + ~FileStreamImpl() + { + // it looks like having this finalizer is mandatory, + // as we can not guarantee that the Strategy won't be null in FileStream finalizer + Dispose(false); + } + internal override void DisposeInternal(bool disposing) => Dispose(disposing); internal override IntPtr Handle => SafeFileHandle.DangerousGetHandle(); From e3b23fd85c662c4889d51f1aa6e9e7c314eb45af Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 12:28:14 +0100 Subject: [PATCH 040/100] fix a typo in the comments --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 1375ed707a4e7..56dee1d20ca95 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -36,7 +36,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { - // it might seem to have to sense now, but we plan to introduce dedicated strategies for sync and async implementations + // it might seem to have no sense now, but we plan to introduce dedicated strategies for sync and async implementations switch (isAsync) { case true: From 2581181d25880a85d865d81723f074b77af87eea Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 12:29:13 +0100 Subject: [PATCH 041/100] rename _actualImplementation to _impl as suggested in code review --- .../src/System/IO/FileStream.cs | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 56dee1d20ca95..9b87d3bfb38b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -13,7 +13,7 @@ public class FileStream : Stream private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; - private readonly FileStreamStrategy _actualImplementation; + private readonly FileStreamStrategy _impl; [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access) @@ -40,10 +40,10 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS switch (isAsync) { case true: - _actualImplementation = new FileStreamImpl(handle, access, ownsHandle, bufferSize, true); + _impl = new FileStreamImpl(handle, access, ownsHandle, bufferSize, true); return; case false: - _actualImplementation = new FileStreamImpl(handle, access, ownsHandle, bufferSize, false); + _impl = new FileStreamImpl(handle, access, ownsHandle, bufferSize, false); return; } } @@ -63,10 +63,10 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool switch (isAsync) { case true: - _actualImplementation = new FileStreamImpl(handle, access, bufferSize, true); + _impl = new FileStreamImpl(handle, access, bufferSize, true); return; case false: - _actualImplementation = new FileStreamImpl(handle, access, bufferSize, false); + _impl = new FileStreamImpl(handle, access, bufferSize, false); return; } } @@ -95,20 +95,20 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share { if ((options & FileOptions.Asynchronous) != 0) { - _actualImplementation = new FileStreamImpl(path, mode, access, share, bufferSize, options); + _impl = new FileStreamImpl(path, mode, access, share, bufferSize, options); } else { - _actualImplementation = new FileStreamImpl(path, mode, access, share, bufferSize, options); + _impl = new FileStreamImpl(path, mode, access, share, bufferSize, options); } } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle => _actualImplementation.Handle; + public virtual IntPtr Handle => _impl.Handle; - public virtual void Lock(long position, long length) => _actualImplementation.Lock(position, length); + public virtual void Lock(long position, long length) => _impl.Lock(position, length); - public virtual void Unlock(long position, long length) => _actualImplementation.Unlock(position, length); + public virtual void Unlock(long position, long length) => _impl.Unlock(position, length); public override Task FlushAsync(CancellationToken cancellationToken) { @@ -119,21 +119,21 @@ public override Task FlushAsync(CancellationToken cancellationToken) if (GetType() != typeof(FileStream)) return base.FlushAsync(cancellationToken); - return _actualImplementation.FlushAsync(cancellationToken); + return _impl.FlushAsync(cancellationToken); } public override int Read(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); - return _actualImplementation.Read(buffer, offset, count); + return _impl.Read(buffer, offset, count); } public override int Read(Span buffer) { - if (GetType() == typeof(FileStream) && !_actualImplementation.IsAsync) + if (GetType() == typeof(FileStream) && !_impl.IsAsync) { - return _actualImplementation.Read(buffer); + return _impl.Read(buffer); } else { @@ -163,10 +163,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) throw Error.GetFileNotOpen(); - return _actualImplementation.ReadAsync(buffer, offset, count, cancellationToken); + return _impl.ReadAsync(buffer, offset, count, cancellationToken); } public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -183,31 +183,31 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken return ValueTask.FromCanceled(cancellationToken); } - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) { throw Error.GetFileNotOpen(); } - return _actualImplementation.ReadAsync(buffer, cancellationToken); + return _impl.ReadAsync(buffer, cancellationToken); } public override void Write(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); - _actualImplementation.Write(buffer, offset, count); + _impl.Write(buffer, offset, count); } public override void Write(ReadOnlySpan buffer) { - if (GetType() == typeof(FileStream) && !_actualImplementation.IsAsync) + if (GetType() == typeof(FileStream) && !_impl.IsAsync) { - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) { throw Error.GetFileNotOpen(); } - _actualImplementation.Write(buffer); + _impl.Write(buffer); } else { @@ -237,10 +237,10 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) throw Error.GetFileNotOpen(); - return _actualImplementation.WriteAsync(buffer, offset, count, cancellationToken); + return _impl.WriteAsync(buffer, offset, count, cancellationToken); } public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) @@ -257,18 +257,18 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return ValueTask.FromCanceled(cancellationToken); } - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) { throw Error.GetFileNotOpen(); } - return _actualImplementation.WriteAsync(buffer, cancellationToken); + return _impl.WriteAsync(buffer, cancellationToken); } /// /// Clears buffers for this stream and causes any buffered data to be written to the file. /// - public override void Flush() => _actualImplementation.Flush(); + public override void Flush() => _impl.Flush(); /// /// Clears buffers for this stream, and if is true, @@ -276,16 +276,16 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo /// public virtual void Flush(bool flushToDisk) { - if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); + if (_impl.IsClosed) throw Error.GetFileNotOpen(); - _actualImplementation.Flush(flushToDisk); + _impl.Flush(flushToDisk); } /// Gets a value indicating whether the current stream supports reading. - public override bool CanRead => _actualImplementation.CanRead; + public override bool CanRead => _impl.CanRead; /// Gets a value indicating whether the current stream supports writing. - public override bool CanWrite => _actualImplementation.CanWrite; + public override bool CanWrite => _impl.CanWrite; /// Validates arguments to Read and Write and throws resulting exceptions. /// The buffer to read from or write to. @@ -294,7 +294,7 @@ public virtual void Flush(bool flushToDisk) private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) throw Error.GetFileNotOpen(); } @@ -304,32 +304,32 @@ public override void SetLength(long value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) throw Error.GetFileNotOpen(); - if (!_actualImplementation.CanSeek) + if (!_impl.CanSeek) throw Error.GetSeekNotSupported(); - if (!_actualImplementation.CanWrite) + if (!_impl.CanWrite) throw Error.GetWriteNotSupported(); - _actualImplementation.SetLength(value); + _impl.SetLength(value); } - public virtual SafeFileHandle SafeFileHandle => _actualImplementation.SafeFileHandle; + public virtual SafeFileHandle SafeFileHandle => _impl.SafeFileHandle; /// Gets the path that was passed to the constructor. - public virtual string Name => _actualImplementation.Name; + public virtual string Name => _impl.Name; /// Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously. - public virtual bool IsAsync => _actualImplementation.IsAsync; + public virtual bool IsAsync => _impl.IsAsync; /// Gets the length of the stream in bytes. public override long Length { get { - if (_actualImplementation.IsClosed) throw Error.GetFileNotOpen(); - if (!_actualImplementation.CanSeek) throw Error.GetSeekNotSupported(); - return _actualImplementation.Length; + if (_impl.IsClosed) throw Error.GetFileNotOpen(); + if (!_impl.CanSeek) throw Error.GetSeekNotSupported(); + return _impl.Length; } } @@ -338,37 +338,37 @@ public override long Position { get { - if (_actualImplementation.IsClosed) + if (_impl.IsClosed) throw Error.GetFileNotOpen(); - if (!_actualImplementation.CanSeek) + if (!_impl.CanSeek) throw Error.GetSeekNotSupported(); - return _actualImplementation.Position; + return _impl.Position; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - _actualImplementation.Seek(value, SeekOrigin.Begin); + _impl.Seek(value, SeekOrigin.Begin); } } - internal virtual bool IsClosed => _actualImplementation.IsClosed; + internal virtual bool IsClosed => _impl.IsClosed; /// /// Reads a byte from the file stream. Returns the byte cast to an int /// or -1 if reading from the end of the stream. /// - public override int ReadByte() => _actualImplementation.ReadByte(); + public override int ReadByte() => _impl.ReadByte(); /// /// Writes a byte to the current position in the stream and advances the position /// within the stream by one byte. /// /// The byte to write to the stream. - public override void WriteByte(byte value) => _actualImplementation.WriteByte(value); + public override void WriteByte(byte value) => _impl.WriteByte(value); ~FileStream() { @@ -380,9 +380,9 @@ public override long Position protected override void Dispose(bool disposing) { - if (_actualImplementation != null) // possible in finalizer + if (_impl != null) // possible in finalizer { - _actualImplementation.DisposeInternal(disposing); + _impl.DisposeInternal(disposing); } } @@ -393,33 +393,33 @@ public override ValueTask DisposeAsync() return base.DisposeAsync(); } - return _actualImplementation.DisposeAsync(); + return _impl.DisposeAsync(); } - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _actualImplementation.CopyToAsync(destination, bufferSize, cancellationToken); + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _impl.CopyToAsync(destination, bufferSize, cancellationToken); public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (_actualImplementation.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!_actualImplementation.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + if (_impl.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!_impl.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - if (!_actualImplementation.IsAsync) + if (!_impl.IsAsync) return base.BeginRead(buffer, offset, count, callback, state); else - return _actualImplementation.BeginRead(buffer, offset, count, callback, state); + return _impl.BeginRead(buffer, offset, count, callback, state); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (_actualImplementation.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!_actualImplementation.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + if (_impl.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!_impl.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - if (!_actualImplementation.IsAsync) + if (!_impl.IsAsync) return base.BeginWrite(buffer, offset, count, callback, state); else - return _actualImplementation.BeginWrite(buffer, offset, count, callback, state); + return _impl.BeginWrite(buffer, offset, count, callback, state); } public override int EndRead(IAsyncResult asyncResult) @@ -427,7 +427,7 @@ public override int EndRead(IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - if (!_actualImplementation.IsAsync) + if (!_impl.IsAsync) return base.EndRead(asyncResult); else return TaskToApm.End(asyncResult); @@ -438,14 +438,14 @@ public override void EndWrite(IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - if (!_actualImplementation.IsAsync) + if (!_impl.IsAsync) base.EndWrite(asyncResult); else TaskToApm.End(asyncResult); } - public override bool CanSeek => _actualImplementation.CanSeek; + public override bool CanSeek => _impl.CanSeek; - public override long Seek(long offset, SeekOrigin origin) => _actualImplementation.Seek(offset, origin); + public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); } } From 62580e85531e3ed5c258360c387e02e7cb28102b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 12:59:09 +0100 Subject: [PATCH 042/100] move ctor argument validation logic back to FileStream (it's going to make it easier to separate Windows and Unix implementation) --- .../src/System/IO/FileStream.cs | 48 +++++++++++++++++++ .../src/System/IO/FileStreamImpl.cs | 48 ------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 9b87d3bfb38b0..34a2c5ca4dc1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -13,6 +14,9 @@ public class FileStream : Stream private const FileShare DefaultShare = FileShare.Read; private const bool DefaultIsAsync = false; + /// Caches whether Serialization Guard has been disabled for file writes + private static int s_cachedSerializationSwitch; + private readonly FileStreamStrategy _impl; [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] @@ -93,6 +97,50 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { + if (path == null) + throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); + if (path.Length == 0) + throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); + + // don't include inheritable in our bounds check for share + FileShare tempshare = share & ~FileShare.Inheritable; + string? badArg = null; + + if (mode < FileMode.CreateNew || mode > FileMode.Append) + badArg = nameof(mode); + else if (access < FileAccess.Read || access > FileAccess.ReadWrite) + badArg = nameof(access); + else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) + badArg = nameof(share); + + if (badArg != null) + throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); + + // NOTE: any change to FileOptions enum needs to be matched here in the error validation + if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) + throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); + + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + // Write access validation + if ((access & FileAccess.Write) == 0) + { + if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) + { + // No write access, mode and access disagree but flag access since mode comes first + throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access)); + } + } + + if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) + throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); + + if ((access & FileAccess.Write) == FileAccess.Write) + { + SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); + } + if ((options & FileOptions.Asynchronous) != 0) { _impl = new FileStreamImpl(path, mode, access, share, bufferSize, options); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index ec22969c5d460..f8694060b7483 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -63,9 +62,6 @@ internal sealed partial class FileStreamImpl : FileStreamStrategy /// Whether the file stream's handle has been exposed. private bool _exposedHandle; - /// Caches whether Serialization Guard has been disabled for file writes - private static int s_cachedSerializationSwitch; - internal FileStreamImpl(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); @@ -133,45 +129,6 @@ internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { - if (path == null) - throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); - if (path.Length == 0) - throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - - // don't include inheritable in our bounds check for share - FileShare tempshare = share & ~FileShare.Inheritable; - string? badArg = null; - - if (mode < FileMode.CreateNew || mode > FileMode.Append) - badArg = nameof(mode); - else if (access < FileAccess.Read || access > FileAccess.ReadWrite) - badArg = nameof(access); - else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) - badArg = nameof(share); - - if (badArg != null) - throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); - - // NOTE: any change to FileOptions enum needs to be matched here in the error validation - if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) - throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); - - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); - - // Write access validation - if ((access & FileAccess.Write) == 0) - { - if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) - { - // No write access, mode and access disagree but flag access since mode comes first - throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access)); - } - } - - if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) - throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); - string fullPath = Path.GetFullPath(path); _path = fullPath; @@ -181,11 +138,6 @@ internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare if ((options & FileOptions.Asynchronous) != 0) _useAsyncIO = true; - if ((access & FileAccess.Write) == FileAccess.Write) - { - SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); - } - _fileHandle = OpenHandle(mode, share, options); try From 82ac0d7b41fe5fc685f60d79f9a85a781e6a0705 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 13:43:34 +0100 Subject: [PATCH 043/100] move handle validation logic to FileStream, make _bufferLength readonly --- .../src/System/IO/FileStream.cs | 44 +++++++++++++++-- .../src/System/IO/FileStreamImpl.cs | 49 +++---------------- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 34a2c5ca4dc1d..31dc16e6fe131 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -40,18 +40,52 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { + SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); + try + { + ValidateHandle(safeHandle, access, bufferSize, isAsync); + } + catch + { + // We don't want to take ownership of closing passed in handles + // *unless* the constructor completes successfully. + GC.SuppressFinalize(safeHandle); + + // This would also prevent Close from being called, but is unnecessary + // as we've removed the object from the finalizer queue. + // + // safeHandle.SetHandleAsInvalid(); + throw; + } + // it might seem to have no sense now, but we plan to introduce dedicated strategies for sync and async implementations switch (isAsync) { case true: - _impl = new FileStreamImpl(handle, access, ownsHandle, bufferSize, true); + _impl = new FileStreamImpl(safeHandle, true, access, bufferSize, true); return; case false: - _impl = new FileStreamImpl(handle, access, ownsHandle, bufferSize, false); + _impl = new FileStreamImpl(safeHandle, true, access, bufferSize, false); return; } } + private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + { + if (handle.IsInvalid) + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + if (handle.IsClosed) + throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault()) + throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); + } + public FileStream(SafeFileHandle handle, FileAccess access) : this(handle, access, DefaultBufferSize) { @@ -64,13 +98,15 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { + ValidateHandle(handle, access, bufferSize, isAsync); + switch (isAsync) { case true: - _impl = new FileStreamImpl(handle, access, bufferSize, true); + _impl = new FileStreamImpl(handle, false, access, bufferSize, true); return; case false: - _impl = new FileStreamImpl(handle, access, bufferSize, false); + _impl = new FileStreamImpl(handle, false, access, bufferSize, false); return; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index f8694060b7483..15bab76bb863d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -14,7 +14,7 @@ internal sealed partial class FileStreamImpl : FileStreamStrategy private const bool DefaultIsAsync = false; private byte[]? _buffer; - private int _bufferLength; + private readonly int _bufferLength; private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws /// Whether the file is opened for reading, writing, or both. @@ -62,18 +62,20 @@ internal sealed partial class FileStreamImpl : FileStreamStrategy /// Whether the file stream's handle has been exposed. private bool _exposedHandle; - internal FileStreamImpl(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) + internal FileStreamImpl(SafeFileHandle handle, bool dontOwnHandle, FileAccess access, int bufferSize, bool isAsync) { - SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); + _exposedHandle = true; + _bufferLength = bufferSize; + try { - ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync); + InitFromHandle(handle, access, isAsync); } - catch + catch when (dontOwnHandle) { // We don't want to take ownership of closing passed in handles // *unless* the constructor completes successfully. - GC.SuppressFinalize(safeHandle); + GC.SuppressFinalize(handle); // This would also prevent Close from being called, but is unnecessary // as we've removed the object from the finalizer queue. @@ -87,41 +89,6 @@ internal FileStreamImpl(IntPtr handle, FileAccess access, bool ownsHandle, int b _access = access; _useAsyncIO = isAsync; - // As the handle was passed in, we must set the handle field at the very end to - // avoid the finalizer closing the handle when we throw errors. - _fileHandle = safeHandle; - } - - private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - { - if (handle.IsInvalid) - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); - - if (handle.IsClosed) - throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault()) - throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); - - _exposedHandle = true; - _bufferLength = bufferSize; - - InitFromHandle(handle, access, isAsync); - } - - internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - { - ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); - - // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, - // but we can't as they're readonly. - _access = access; - _useAsyncIO = isAsync; - // As the handle was passed in, we must set the handle field at the very end to // avoid the finalizer closing the handle when we throw errors. _fileHandle = handle; From d35bd158f4cc3443daf4595a75dac9bc62a6adf3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 14:04:19 +0100 Subject: [PATCH 044/100] move more validation logic to FileStream --- .../src/System/IO/FileStream.cs | 44 +++++++++++++++- .../src/System/IO/FileStreamImpl.cs | 50 ++----------------- 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 31dc16e6fe131..0f1e1cdc39688 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -190,9 +190,35 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] public virtual IntPtr Handle => _impl.Handle; - public virtual void Lock(long position, long length) => _impl.Lock(position, length); + public virtual void Lock(long position, long length) + { + if (position < 0 || length < 0) + { + throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_impl.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + _impl.Lock(position, length); + } + + public virtual void Unlock(long position, long length) + { + if (position < 0 || length < 0) + { + throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_impl.IsClosed) + { + throw Error.GetFileNotOpen(); + } - public virtual void Unlock(long position, long length) => _impl.Unlock(position, length); + _impl.Unlock(position, length); + } public override Task FlushAsync(CancellationToken cancellationToken) { @@ -203,6 +229,15 @@ public override Task FlushAsync(CancellationToken cancellationToken) if (GetType() != typeof(FileStream)) return base.FlushAsync(cancellationToken); + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if (_impl.IsClosed) + { + throw Error.GetFileNotOpen(); + } + return _impl.FlushAsync(cancellationToken); } @@ -217,6 +252,11 @@ public override int Read(Span buffer) { if (GetType() == typeof(FileStream) && !_impl.IsAsync) { + if (_impl.IsClosed) + { + throw Error.GetFileNotOpen(); + } + return _impl.Read(buffer); } else diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 15bab76bb863d..9b41aa8a8a155 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -132,47 +132,12 @@ internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare internal override IntPtr Handle => SafeFileHandle.DangerousGetHandle(); - internal override void Lock(long position, long length) - { - if (position < 0 || length < 0) - { - throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); - } - - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - - LockInternal(position, length); - } - - internal override void Unlock(long position, long length) - { - if (position < 0 || length < 0) - { - throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); - } - - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } + internal override void Lock(long position, long length) => LockInternal(position, length); - UnlockInternal(position, length); - } + internal override void Unlock(long position, long length) => UnlockInternal(position, length); public override Task FlushAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - // TODO: https://github.com/dotnet/runtime/issues/27643 (stop doing this synchronous work!!). // The always synchronous data transfer between the OS and the internal buffer is intentional // because this is needed to allow concurrent async IO requests. Concurrent data transfer @@ -200,16 +165,7 @@ public override int Read(byte[] buffer, int offset, int count) ReadSpan(new Span(buffer, offset, count)); } - public override int Read(Span buffer) - { - Debug.Assert(!_useAsyncIO); - - if (_fileHandle.IsClosed) - { - throw Error.GetFileNotOpen(); - } - return ReadSpan(buffer); - } + public override int Read(Span buffer) => ReadSpan(buffer); public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { From 7a4c761fffe1a436e4e261c1e94d71bd764d8cf3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 14:32:08 +0100 Subject: [PATCH 045/100] more polishing --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Win32.cs | 4 ++-- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 7 +------ 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 0f1e1cdc39688..f0a8cf491438a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -92,7 +92,7 @@ public FileStream(SafeFileHandle handle, FileAccess access) } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, FileStreamImpl.GetDefaultIsAsync(handle)) + : this(handle, access, bufferSize, FileStreamImpl.GetDefaultIsAsync(handle, DefaultIsAsync)) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index ea3e21bd2c7b1..51578bbfa104c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -61,7 +61,7 @@ private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions op return SafeFileHandle.Open(_path!, openFlags, (int)OpenPermissions); } - internal static bool GetDefaultIsAsync(SafeFileHandle handle) => handle.IsAsync ?? DefaultIsAsync; + internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsync) => handle.IsAsync ?? defaultIsAsync; /// Initializes a stream for reading or writing a Unix file. /// How the file should be opened. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs index c2f43b7c81ff2..836569a6b3920 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs @@ -45,9 +45,9 @@ private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare shar } } - internal static bool GetDefaultIsAsync(SafeFileHandle handle) + internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsync) { - return handle.IsAsync ?? !IsHandleSynchronous(handle, ignoreInvalid: true) ?? DefaultIsAsync; + return handle.IsAsync ?? !IsHandleSynchronous(handle, ignoreInvalid: true) ?? defaultIsAsync; } private static unsafe bool? IsHandleSynchronous(SafeFileHandle fileHandle, bool ignoreInvalid) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 9b41aa8a8a155..cc7e2a63195bd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -11,8 +11,6 @@ namespace System.IO { internal sealed partial class FileStreamImpl : FileStreamStrategy { - private const bool DefaultIsAsync = false; - private byte[]? _buffer; private readonly int _bufferLength; private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws @@ -230,10 +228,7 @@ public override void Write(byte[] buffer, int offset, int count) } } - public override void Write(ReadOnlySpan buffer) - { - WriteSpan(buffer); - } + public override void Write(ReadOnlySpan buffer) => WriteSpan(buffer); public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { From 7c95a6c9bf7280dace122173a4ef088e693a3820 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 15:25:35 +0100 Subject: [PATCH 046/100] reduce code duplication in ctors --- .../src/System/IO/FileStream.cs | 26 +++++++++---------- .../src/System/IO/FileStreamImpl.cs | 19 ++------------ 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index f0a8cf491438a..383f7745683c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -44,6 +44,17 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS try { ValidateHandle(safeHandle, access, bufferSize, isAsync); + + // it might seem to have no sense now, but we plan to introduce dedicated strategies for sync and async implementations + switch (isAsync) + { + case true: + _impl = new FileStreamImpl(safeHandle, access, bufferSize, true); + return; + case false: + _impl = new FileStreamImpl(safeHandle, access, bufferSize, false); + return; + } } catch { @@ -57,17 +68,6 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS // safeHandle.SetHandleAsInvalid(); throw; } - - // it might seem to have no sense now, but we plan to introduce dedicated strategies for sync and async implementations - switch (isAsync) - { - case true: - _impl = new FileStreamImpl(safeHandle, true, access, bufferSize, true); - return; - case false: - _impl = new FileStreamImpl(safeHandle, true, access, bufferSize, false); - return; - } } private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) @@ -103,10 +103,10 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool switch (isAsync) { case true: - _impl = new FileStreamImpl(handle, false, access, bufferSize, true); + _impl = new FileStreamImpl(handle, access, bufferSize, true); return; case false: - _impl = new FileStreamImpl(handle, false, access, bufferSize, false); + _impl = new FileStreamImpl(handle, access, bufferSize, false); return; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index cc7e2a63195bd..cd22c314ea7b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -60,27 +60,12 @@ internal sealed partial class FileStreamImpl : FileStreamStrategy /// Whether the file stream's handle has been exposed. private bool _exposedHandle; - internal FileStreamImpl(SafeFileHandle handle, bool dontOwnHandle, FileAccess access, int bufferSize, bool isAsync) + internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { _exposedHandle = true; _bufferLength = bufferSize; - try - { - InitFromHandle(handle, access, isAsync); - } - catch when (dontOwnHandle) - { - // We don't want to take ownership of closing passed in handles - // *unless* the constructor completes successfully. - GC.SuppressFinalize(handle); - - // This would also prevent Close from being called, but is unnecessary - // as we've removed the object from the finalizer queue. - // - // safeHandle.SetHandleAsInvalid(); - throw; - } + InitFromHandle(handle, access, isAsync); // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, // but we can't as they're readonly. From 85ab921499aac432027403b95ace0f2607dfba7b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 19 Jan 2021 15:33:23 +0100 Subject: [PATCH 047/100] the Unix implementation was assuming type check is not needed https://github.com/dotnet/runtime/blob/72e21094581b7882cbd57cb1f66474210c82ed62/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs#L303-L305 --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 383f7745683c0..a8b9a1746f1e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -512,7 +512,7 @@ protected override void Dispose(bool disposing) public override ValueTask DisposeAsync() { - if (OperatingSystem.IsWindows() && GetType() != typeof(FileStream)) + if (GetType() != typeof(FileStream)) { return base.DisposeAsync(); } From ccfea9a025c34269eda5040ca75e7db1929294ff Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 20 Jan 2021 13:45:56 +0100 Subject: [PATCH 048/100] fix the Flush bug that I've introduced --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index a8b9a1746f1e2..2f6479e99f03f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -392,7 +392,11 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo /// /// Clears buffers for this stream and causes any buffered data to be written to the file. /// - public override void Flush() => _impl.Flush(); + public override void Flush() + { + // Make sure that we call through the public virtual API + Flush(flushToDisk: false); + } /// /// Clears buffers for this stream, and if is true, From 97a8c51c3599e1fdc6a92f727d7c139591d1da7f Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 20 Jan 2021 14:01:40 +0100 Subject: [PATCH 049/100] make sure base.CopyToAsync is called for all custom types that Derive from FileStream (because the method calls other base methods) --- .../src/System/IO/FileStream.cs | 25 +++++++++++++------ .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.Windows.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 4 +-- .../src/System/IO/FileStreamStrategy.cs | 4 +++ 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 2f6479e99f03f..9305e839c208a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -49,10 +49,10 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS switch (isAsync) { case true: - _impl = new FileStreamImpl(safeHandle, access, bufferSize, true); + _impl = new FileStreamImpl(this, safeHandle, access, bufferSize, true); return; case false: - _impl = new FileStreamImpl(safeHandle, access, bufferSize, false); + _impl = new FileStreamImpl(this, safeHandle, access, bufferSize, false); return; } } @@ -103,10 +103,10 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool switch (isAsync) { case true: - _impl = new FileStreamImpl(handle, access, bufferSize, true); + _impl = new FileStreamImpl(this, handle, access, bufferSize, true); return; case false: - _impl = new FileStreamImpl(handle, access, bufferSize, false); + _impl = new FileStreamImpl(this, handle, access, bufferSize, false); return; } } @@ -179,11 +179,11 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share if ((options & FileOptions.Asynchronous) != 0) { - _impl = new FileStreamImpl(path, mode, access, share, bufferSize, options); + _impl = new FileStreamImpl(this, path, mode, access, share, bufferSize, options); } else { - _impl = new FileStreamImpl(path, mode, access, share, bufferSize, options); + _impl = new FileStreamImpl(this, path, mode, access, share, bufferSize, options); } } @@ -524,7 +524,15 @@ public override ValueTask DisposeAsync() return _impl.DisposeAsync(); } - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _impl.CopyToAsync(destination, bufferSize, cancellationToken); + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + if (GetType() != typeof(FileStream)) + { + base.CopyToAsync(destination, bufferSize, cancellationToken); + } + + return _impl.CopyToAsync(destination, bufferSize, cancellationToken); + } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { @@ -575,5 +583,8 @@ public override void EndWrite(IAsyncResult asyncResult) public override bool CanSeek => _impl.CanSeek; public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); + + internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + => base.CopyToAsync(destination, bufferSize, cancellationToken); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index 51578bbfa104c..d5a7267b97054 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -706,7 +706,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => // Windows version overrides this method, so the Unix version does as well, but it doesn't // currently have any special optimizations to be done and so just calls to the base. - base.CopyToAsync(destination, bufferSize, cancellationToken); + _fileStream.BaseCopyToAsync(destination, bufferSize, cancellationToken); /// Sets the current position of this stream to the given value. /// The point relative to origin from which to begin seeking. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index ea6ff7a138e69..c103f90dfe6c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -1244,7 +1244,7 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio // case our custom CopyToAsync implementation isn't necessarily correct. if (!_useAsyncIO) { - return base.CopyToAsync(destination, bufferSize, cancellationToken); + return _fileStream.BaseCopyToAsync(destination, bufferSize, cancellationToken); } ValidateCopyToArguments(destination, bufferSize); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index cd22c314ea7b5..d4f950f2543a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -60,7 +60,7 @@ internal sealed partial class FileStreamImpl : FileStreamStrategy /// Whether the file stream's handle has been exposed. private bool _exposedHandle; - internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + internal FileStreamImpl(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) { _exposedHandle = true; _bufferLength = bufferSize; @@ -77,7 +77,7 @@ internal FileStreamImpl(SafeFileHandle handle, FileAccess access, int bufferSize _fileHandle = handle; } - internal FileStreamImpl(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + internal FileStreamImpl(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) { string fullPath = Path.GetFullPath(path); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs index f0e5df36c701f..33e9a7512b99f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs @@ -7,6 +7,10 @@ namespace System.IO { internal abstract class FileStreamStrategy : Stream { + protected readonly FileStream _fileStream; + + protected FileStreamStrategy(FileStream fileStream) => _fileStream = fileStream; + internal abstract bool IsAsync { get; } internal abstract string Name { get; } From a288c2aa9246ac27d32cce36a507146e321ca4b5 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 20 Jan 2021 14:20:03 +0100 Subject: [PATCH 050/100] introduce DerivedFileStreamImpl, remove GetType checks from FileStream --- .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/IO/DerivedFileStreamImpl.cs | 127 ++++++++++++++++ .../src/System/IO/FileStream.cs | 138 +++++------------- .../src/System/IO/FileStreamImpl.cs | 32 +++- 4 files changed, 192 insertions(+), 106 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 765fe70f2aa99..a6c99422b547a 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -396,6 +396,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs new file mode 100644 index 0000000000000..ee63fffd052a5 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs @@ -0,0 +1,127 @@ +ï»ż// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO +{ + // this type exists so we can avoid GetType() != typeof(FileStream) checks in FileStream + // when FileStream was supposed to call base.Method() for such cases, we just call _fileStream.BaseMethod() + // for everything else we fall back to the actual strategy (like FileStream does) + // + // it's crucial to NOT use the "base" keyoword here! everything must be using _fileStream or _impl + internal sealed class DerivedFileStreamImpl : FileStreamStrategy + { + private readonly FileStreamStrategy _impl; + + internal DerivedFileStreamImpl(FileStream fileStream, FileStreamStrategy impl) : base(fileStream) => _impl = impl; + + public override bool CanRead => _impl.CanRead; + + public override bool CanWrite => _impl.CanWrite; + + public override bool CanSeek => _impl.CanSeek; + + public override long Length => _impl.Length; + + public override long Position + { + get => _impl.Position; + set => _impl.Position = value; + } + + internal override bool IsAsync => _impl.IsAsync; + + internal override string Name => _impl.Name; + + internal override IntPtr Handle => _impl.Handle; + + internal override SafeFileHandle SafeFileHandle => _impl.SafeFileHandle; + + internal override bool IsClosed => _impl.IsClosed; + + internal override void Lock(long position, long length) => _impl.Lock(position, length); + + internal override void Unlock(long position, long length) => _impl.Unlock(position, length); + + public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); + + public override void SetLength(long value) => _impl.SetLength(value); + + public override int ReadByte() => _impl.ReadByte(); + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => _impl.BeginRead(buffer, offset, count, callback, state); + + public override int Read(byte[] buffer, int offset, int count) => _impl.Read(buffer, offset, count); + + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) + // overload being introduced. In that case, this Read(Span) overload should use the behavior + // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to + // Read(byte[],int,int), which will do the right thing if we're in async mode. + public override int Read(Span buffer) + => _fileStream.BaseRead(buffer); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Read() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => _fileStream.BaseReadAsync(buffer, offset, count, cancellationToken); + + // If this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + => _fileStream.BaseReadAsync(buffer, cancellationToken); + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => _impl.BeginRead(buffer, offset, count, callback, state); + + public override void WriteByte(byte value) => _impl.WriteByte(value); + + public override void Write(byte[] buffer, int offset, int count) => _impl.Write(buffer, offset, count); + + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) + // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior + // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to + // Write(byte[],int,int), which will do the right thing if we're in async mode. + public override void Write(ReadOnlySpan buffer) + => _fileStream.BaseWrite(buffer); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => _fileStream.BaseWriteAsync(buffer, offset, count, cancellationToken); + + // If this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => _fileStream.BaseWriteAsync(buffer, cancellationToken); + + public override void Flush() => _impl.Flush(); + + internal override void Flush(bool flushToDisk) => _impl.Flush(flushToDisk); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Flush() which a subclass might have overridden. To be safe + // we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Flush) when we are not sure. + public override Task FlushAsync(CancellationToken cancellationToken) + => _fileStream.BaseFlushAsync(cancellationToken); + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + => _fileStream.BaseCopyToAsync(destination, bufferSize, cancellationToken); + + public override ValueTask DisposeAsync() => _fileStream.BaseDisposeAsync(); + + internal override void DisposeInternal(bool disposing) => _impl.DisposeInternal(disposing); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 9305e839c208a..a1eaa0bc08b52 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -49,10 +49,10 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS switch (isAsync) { case true: - _impl = new FileStreamImpl(this, safeHandle, access, bufferSize, true); + _impl = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, true)); return; case false: - _impl = new FileStreamImpl(this, safeHandle, access, bufferSize, false); + _impl = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, false)); return; } } @@ -86,6 +86,9 @@ private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); } + private FileStreamStrategy WrapForDerivedType(FileStreamStrategy impl) + => GetType() == typeof(FileStream) ? impl : new DerivedFileStreamImpl(this, impl); + public FileStream(SafeFileHandle handle, FileAccess access) : this(handle, access, DefaultBufferSize) { @@ -103,10 +106,10 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool switch (isAsync) { case true: - _impl = new FileStreamImpl(this, handle, access, bufferSize, true); + _impl = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, true)); return; case false: - _impl = new FileStreamImpl(this, handle, access, bufferSize, false); + _impl = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, false)); return; } } @@ -179,11 +182,11 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share if ((options & FileOptions.Asynchronous) != 0) { - _impl = new FileStreamImpl(this, path, mode, access, share, bufferSize, options); + _impl = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); } else { - _impl = new FileStreamImpl(this, path, mode, access, share, bufferSize, options); + _impl = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); } } @@ -222,13 +225,6 @@ public virtual void Unlock(long position, long length) public override Task FlushAsync(CancellationToken cancellationToken) { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Flush() which a subclass might have overridden. To be safe - // we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Flush) when we are not sure. - if (GetType() != typeof(FileStream)) - return base.FlushAsync(cancellationToken); - if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); @@ -241,6 +237,9 @@ public override Task FlushAsync(CancellationToken cancellationToken) return _impl.FlushAsync(cancellationToken); } + internal Task BaseFlushAsync(CancellationToken cancellationToken) + => base.FlushAsync(cancellationToken); + public override int Read(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); @@ -248,42 +247,14 @@ public override int Read(byte[] buffer, int offset, int count) return _impl.Read(buffer, offset, count); } - public override int Read(Span buffer) - { - if (GetType() == typeof(FileStream) && !_impl.IsAsync) - { - if (_impl.IsClosed) - { - throw Error.GetFileNotOpen(); - } + public override int Read(Span buffer) => _impl.Read(buffer); - return _impl.Read(buffer); - } - else - { - // This type is derived from FileStream and/or the stream is in async mode. If this is a - // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) - // overload being introduced. In that case, this Read(Span) overload should use the behavior - // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the - // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to - // Read(byte[],int,int), which will do the right thing if we're in async mode. - return base.Read(buffer); - } - } + internal int BaseRead(Span buffer) => base.Read(buffer); public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ValidateBufferArguments(buffer, offset, count); - if (GetType() != typeof(FileStream)) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() which a subclass might have overridden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. - return base.ReadAsync(buffer, offset, count, cancellationToken); - } - if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); @@ -293,15 +264,11 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel return _impl.ReadAsync(buffer, offset, count, cancellationToken); } + internal Task BaseReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => base.ReadAsync(buffer, offset, count, cancellationToken); + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { - if (GetType() != typeof(FileStream)) - { - // If this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), - // which was introduced first, so delegate to the base which will delegate to that. - return base.ReadAsync(buffer, cancellationToken); - } - if (cancellationToken.IsCancellationRequested) { return ValueTask.FromCanceled(cancellationToken); @@ -315,6 +282,9 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken return _impl.ReadAsync(buffer, cancellationToken); } + internal ValueTask BaseReadAsync(Memory buffer, CancellationToken cancellationToken = default) + => base.ReadAsync(buffer, cancellationToken); + public override void Write(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); @@ -322,42 +292,14 @@ public override void Write(byte[] buffer, int offset, int count) _impl.Write(buffer, offset, count); } - public override void Write(ReadOnlySpan buffer) - { - if (GetType() == typeof(FileStream) && !_impl.IsAsync) - { - if (_impl.IsClosed) - { - throw Error.GetFileNotOpen(); - } + public override void Write(ReadOnlySpan buffer) => _impl.Write(buffer); - _impl.Write(buffer); - } - else - { - // This type is derived from FileStream and/or the stream is in async mode. If this is a - // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) - // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior - // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the - // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to - // Write(byte[],int,int), which will do the right thing if we're in async mode. - base.Write(buffer); - } - } + internal void BaseWrite(ReadOnlySpan buffer) => base.Write(buffer); public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ValidateBufferArguments(buffer, offset, count); - if (GetType() != typeof(FileStream)) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. - return base.WriteAsync(buffer, offset, count, cancellationToken); - } - if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); @@ -367,15 +309,11 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati return _impl.WriteAsync(buffer, offset, count, cancellationToken); } + internal Task BaseWriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => base.WriteAsync(buffer, offset, count, cancellationToken); + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { - if (GetType() != typeof(FileStream)) - { - // If this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), - // which was introduced first, so delegate to the base which will delegate to that. - return base.WriteAsync(buffer, cancellationToken); - } - if (cancellationToken.IsCancellationRequested) { return ValueTask.FromCanceled(cancellationToken); @@ -389,6 +327,9 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return _impl.WriteAsync(buffer, cancellationToken); } + internal ValueTask BaseWriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => base.WriteAsync(buffer, cancellationToken); + /// /// Clears buffers for this stream and causes any buffered data to be written to the file. /// @@ -514,25 +455,15 @@ protected override void Dispose(bool disposing) } } - public override ValueTask DisposeAsync() - { - if (GetType() != typeof(FileStream)) - { - return base.DisposeAsync(); - } + public override ValueTask DisposeAsync() => _impl.DisposeAsync(); - return _impl.DisposeAsync(); - } + internal ValueTask BaseDisposeAsync() => base.DisposeAsync(); public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - if (GetType() != typeof(FileStream)) - { - base.CopyToAsync(destination, bufferSize, cancellationToken); - } + => _impl.CopyToAsync(destination, bufferSize, cancellationToken); - return _impl.CopyToAsync(destination, bufferSize, cancellationToken); - } + internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + => base.CopyToAsync(destination, bufferSize, cancellationToken); public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { @@ -584,7 +515,6 @@ public override void EndWrite(IAsyncResult asyncResult) public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); - internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - => base.CopyToAsync(destination, bufferSize, cancellationToken); + } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index d4f950f2543a8..4f4af5ccfc60e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -148,7 +148,20 @@ public override int Read(byte[] buffer, int offset, int count) ReadSpan(new Span(buffer, offset, count)); } - public override int Read(Span buffer) => ReadSpan(buffer); + public override int Read(Span buffer) + { + if (!_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + return ReadSpan(buffer); + } + + return _fileStream.BaseRead(buffer); + } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { @@ -213,7 +226,22 @@ public override void Write(byte[] buffer, int offset, int count) } } - public override void Write(ReadOnlySpan buffer) => WriteSpan(buffer); + public override void Write(ReadOnlySpan buffer) + { + if (!_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + WriteSpan(buffer); + } + else + { + _fileStream.BaseWrite(buffer); + } + } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { From 662fa2cec35270bb0ad2ff1fdc1b216f67c2f802 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jan 2021 17:53:39 +0100 Subject: [PATCH 051/100] all FileStream methods should just delegate the actual logic to Strategy --- .../src/System/IO/DerivedFileStreamImpl.cs | 23 +++++++++- .../src/System/IO/FileStream.cs | 44 +++++++++---------- .../src/System/IO/FileStreamImpl.cs | 26 ++++++++++- 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs index ee63fffd052a5..6b8879896209b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs @@ -53,7 +53,12 @@ public override long Position public override int ReadByte() => _impl.ReadByte(); public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => _impl.BeginRead(buffer, offset, count, callback, state); + => _impl.IsAsync + ? _impl.BeginRead(buffer, offset, count, callback, state) + : _fileStream.BaseBeginRead(buffer, offset, count, callback, state); + + public override int EndRead(IAsyncResult asyncResult) + => _impl.IsAsync ? _impl.EndRead(asyncResult) : _fileStream.BaseEndRead(asyncResult); public override int Read(byte[] buffer, int offset, int count) => _impl.Read(buffer, offset, count); @@ -79,7 +84,21 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken => _fileStream.BaseReadAsync(buffer, cancellationToken); public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => _impl.BeginRead(buffer, offset, count, callback, state); + => _impl.IsAsync + ? _impl.BeginWrite(buffer, offset, count, callback, state) + : _fileStream.BaseBeginWrite(buffer, offset, count, callback, state); + + public override void EndWrite(IAsyncResult asyncResult) + { + if (_impl.IsAsync) + { + _impl.EndWrite(asyncResult); + } + else + { + _fileStream.BaseEndWrite(asyncResult); + } + } public override void WriteByte(byte value) => _impl.WriteByte(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index a1eaa0bc08b52..51c8e047089f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -471,50 +471,46 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy if (_impl.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); if (!_impl.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - if (!_impl.IsAsync) - return base.BeginRead(buffer, offset, count, callback, state); - else - return _impl.BeginRead(buffer, offset, count, callback, state); + return _impl.BeginRead(buffer, offset, count, callback, state); } + internal IAsyncResult BaseBeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => base.BeginRead(buffer, offset, count, callback, state); + + public override int EndRead(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + return _impl.EndRead(asyncResult); + } + + internal int BaseEndRead(IAsyncResult asyncResult) => base.EndRead(asyncResult); + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); if (_impl.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); if (!_impl.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - if (!_impl.IsAsync) - return base.BeginWrite(buffer, offset, count, callback, state); - else - return _impl.BeginWrite(buffer, offset, count, callback, state); + return _impl.BeginWrite(buffer, offset, count, callback, state); } - public override int EndRead(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException(nameof(asyncResult)); - - if (!_impl.IsAsync) - return base.EndRead(asyncResult); - else - return TaskToApm.End(asyncResult); - } + internal IAsyncResult BaseBeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => base.BeginWrite(buffer, offset, count, callback, state); public override void EndWrite(IAsyncResult asyncResult) { if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - if (!_impl.IsAsync) - base.EndWrite(asyncResult); - else - TaskToApm.End(asyncResult); + _impl.EndWrite(asyncResult); } + internal void BaseEndWrite(IAsyncResult asyncResult) => base.EndWrite(asyncResult); + public override bool CanSeek => _impl.CanSeek; public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); - - } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 4f4af5ccfc60e..31c0596bc20d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -527,12 +527,34 @@ private void PrepareForWriting() public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { - return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); + if (!_useAsyncIO) + return base.BeginRead(buffer, offset, count, callback, state); + else + return TaskToApm.Begin(ReadAsyncTask(buffer, offset, count, CancellationToken.None), callback, state); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { - return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); + if (!_useAsyncIO) + return base.BeginWrite(buffer, offset, count, callback, state); + else + return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask(), callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + if (!_useAsyncIO) + return base.EndRead(asyncResult); + else + return TaskToApm.End(asyncResult); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + if (!_useAsyncIO) + base.EndWrite(asyncResult); + else + TaskToApm.End(asyncResult); } } } From 233df39f3ce11545af59d5762594089c257db523 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jan 2021 17:53:58 +0100 Subject: [PATCH 052/100] clarify all "base" keyword usages --- .../src/System/IO/DerivedFileStreamImpl.cs | 3 +++ .../src/System/IO/FileStreamImpl.Windows.cs | 6 ++---- .../System.Private.CoreLib/src/System/IO/FileStreamImpl.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs index 6b8879896209b..bbd833600f6c8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs @@ -136,6 +136,9 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo public override Task FlushAsync(CancellationToken cancellationToken) => _fileStream.BaseFlushAsync(cancellationToken); + // We also need to take this path if this is a derived + // instance from FileStream, as a derived type could have overridden ReadAsync, in which + // case our custom CopyToAsync implementation isn't necessarily correct. public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _fileStream.BaseCopyToAsync(destination, bufferSize, cancellationToken); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs index c103f90dfe6c0..b9615cf9f60a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs @@ -1239,12 +1239,10 @@ private int GetLastWin32ErrorAndDisposeHandleIfInvalid() public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { // If we're in sync mode, just use the shared CopyToAsync implementation that does - // typical read/write looping. We also need to take this path if this is a derived - // instance from FileStream, as a derived type could have overridden ReadAsync, in which - // case our custom CopyToAsync implementation isn't necessarily correct. + // typical read/write looping. if (!_useAsyncIO) { - return _fileStream.BaseCopyToAsync(destination, bufferSize, cancellationToken); + return base.CopyToAsync(destination, bufferSize, cancellationToken); } ValidateCopyToArguments(destination, bufferSize); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 31c0596bc20d9..86015a5c3a58e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -266,7 +266,7 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream // rather than something derived from it and what our BeginWrite implementation is going to do. return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + new ValueTask((Task)base.BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : base.WriteAsync(buffer, cancellationToken); } From b4b31c471fd69a23e0e84bf1d804e08f3449b930 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jan 2021 18:29:27 +0100 Subject: [PATCH 053/100] clarify all "base" keyword usages, make sure the comments are in the right place --- .../src/System/IO/DerivedFileStreamImpl.cs | 22 +++++++------------ .../src/System/IO/FileStreamImpl.Unix.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 8 +++++-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs index bbd833600f6c8..655df611e6326 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs @@ -62,19 +62,16 @@ public override int EndRead(IAsyncResult asyncResult) public override int Read(byte[] buffer, int offset, int count) => _impl.Read(buffer, offset, count); - // This type is derived from FileStream and/or the stream is in async mode. If this is a - // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) + // If this is a derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) // overload being introduced. In that case, this Read(Span) overload should use the behavior - // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the - // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to - // Read(byte[],int,int), which will do the right thing if we're in async mode. + // of Read(byte[],int,int) overload. public override int Read(Span buffer) => _fileStream.BaseRead(buffer); - // If we have been inherited into a subclass, the following implementation could be incorrect + // If we have been inherited into a subclass, the Strategy implementation could be incorrect // since it does not call through to Read() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. + // and delegate to FileStream base class (which will call into Read/ReadAsync) when we are not sure. public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => _fileStream.BaseReadAsync(buffer, offset, count, cancellationToken); @@ -104,16 +101,13 @@ public override void EndWrite(IAsyncResult asyncResult) public override void Write(byte[] buffer, int offset, int count) => _impl.Write(buffer, offset, count); - // This type is derived from FileStream and/or the stream is in async mode. If this is a - // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) - // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior - // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the - // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to - // Write(byte[],int,int), which will do the right thing if we're in async mode. + // If this is a derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) + // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior + // of Write(byte[],int,int) overload. public override void Write(ReadOnlySpan buffer) => _fileStream.BaseWrite(buffer); - // If we have been inherited into a subclass, the following implementation could be incorrect + // If we have been inherited into a subclass, the Strategy implementation could be incorrect // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs index d5a7267b97054..51578bbfa104c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs @@ -706,7 +706,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => // Windows version overrides this method, so the Unix version does as well, but it doesn't // currently have any special optimizations to be done and so just calls to the base. - _fileStream.BaseCopyToAsync(destination, bufferSize, cancellationToken); + base.CopyToAsync(destination, bufferSize, cancellationToken); /// Sets the current position of this stream to the given value. /// The point relative to origin from which to begin seeking. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index 86015a5c3a58e..b3853d0194c9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -160,7 +160,9 @@ public override int Read(Span buffer) return ReadSpan(buffer); } - return _fileStream.BaseRead(buffer); + // If the stream is in async mode, we can't call the synchronous ReadSpan, so we similarly call the base Read, + // which will turn delegate to Read(byte[],int,int), which will do the right thing if we're in async mode. + return base.Read(buffer); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -239,7 +241,9 @@ public override void Write(ReadOnlySpan buffer) } else { - _fileStream.BaseWrite(buffer); + // If the stream is in async mode, we can't call the synchronous WriteSpan, so we similarly call the base Write, + // which will turn delegate to Write(byte[],int,int), which will do the right thing if we're in async mode. + base.Write(buffer); } } From 19cec28fbe4a6ea52253d5f9162f03aa9eab81e2 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jan 2021 18:55:45 +0100 Subject: [PATCH 054/100] Flush() called from strategy.SafeFileHandle should call the virtual FileStream.Flush --- .../System.IO.FileSystem/tests/FileStream/Flush.cs | 12 ++++++++++++ .../src/System/IO/DerivedFileStreamImpl.cs | 2 +- .../src/System/IO/FileStreamImpl.cs | 7 ++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs index b9552084b60e3..10e522185726e 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Flush.cs @@ -131,6 +131,18 @@ public void FlushCallsFlush_flushToDisk_False() } } + [Fact] + public void SafeFileHandleCallsFlush_flushToDisk_False() + { + using (StoreFlushArgFileStream fs = new StoreFlushArgFileStream(GetTestFilePath(), FileMode.Create)) + { + GC.KeepAlive(fs.SafeFileHandle); // this should call Flush, which should call StoreFlushArgFileStream.Flush(false) + + Assert.True(fs.LastFlushArg.HasValue); + Assert.False(fs.LastFlushArg.Value); + } + } + [Theory] [InlineData(null)] [InlineData(false)] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs index 655df611e6326..7b0b06f8359cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs @@ -119,7 +119,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => _fileStream.BaseWriteAsync(buffer, cancellationToken); - public override void Flush() => _impl.Flush(); + public override void Flush() => throw new InvalidOperationException("FileStream should never call this method."); internal override void Flush(bool flushToDisk) => _impl.Flush(flushToDisk); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs index b3853d0194c9d..b5cc30ba9449e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs @@ -277,11 +277,8 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return WriteAsyncInternal(buffer, cancellationToken); } - public override void Flush() - { - // Make sure that we call through the public virtual API - Flush(flushToDisk: false); - } + // this method might call Derived type implenentation of Flush(flushToDisk) + public override void Flush() => _fileStream.Flush(); internal override void Flush(bool flushToDisk) { From 3a2f1068fbaf381146554e5c63f6d7286a498b58 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jan 2021 19:25:50 +0100 Subject: [PATCH 055/100] remove the if statement (it's going to become switch) --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 51c8e047089f1..8ec4944b04850 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -180,14 +180,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } - if ((options & FileOptions.Asynchronous) != 0) - { - _impl = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); - } - else - { - _impl = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); - } + _impl = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] From 4e3796ccd72d2128ed4cc7975075793cf15bd7c1 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 10:50:12 +0100 Subject: [PATCH 056/100] get rid of the Impl word, use Strategy everywhere --- .../System.Private.CoreLib.Shared.projitems | 2 +- ...amImpl.cs => DerivedFileStreamStrategy.cs} | 64 ++++----- .../src/System/IO/FileStream.cs | 124 +++++++++--------- 3 files changed, 95 insertions(+), 95 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{DerivedFileStreamImpl.cs => DerivedFileStreamStrategy.cs} (74%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a6c99422b547a..15a9875cd11cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -396,7 +396,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs similarity index 74% rename from src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs index 7b0b06f8359cd..d42ea000877d6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs @@ -11,56 +11,56 @@ namespace System.IO // when FileStream was supposed to call base.Method() for such cases, we just call _fileStream.BaseMethod() // for everything else we fall back to the actual strategy (like FileStream does) // - // it's crucial to NOT use the "base" keyoword here! everything must be using _fileStream or _impl - internal sealed class DerivedFileStreamImpl : FileStreamStrategy + // it's crucial to NOT use the "base" keyoword here! everything must be using _fileStream or _strategy + internal sealed class DerivedFileStreamStrategy : FileStreamStrategy { - private readonly FileStreamStrategy _impl; + private readonly FileStreamStrategy _strategy; - internal DerivedFileStreamImpl(FileStream fileStream, FileStreamStrategy impl) : base(fileStream) => _impl = impl; + internal DerivedFileStreamStrategy(FileStream fileStream, FileStreamStrategy strategy) : base(fileStream) => _strategy = strategy; - public override bool CanRead => _impl.CanRead; + public override bool CanRead => _strategy.CanRead; - public override bool CanWrite => _impl.CanWrite; + public override bool CanWrite => _strategy.CanWrite; - public override bool CanSeek => _impl.CanSeek; + public override bool CanSeek => _strategy.CanSeek; - public override long Length => _impl.Length; + public override long Length => _strategy.Length; public override long Position { - get => _impl.Position; - set => _impl.Position = value; + get => _strategy.Position; + set => _strategy.Position = value; } - internal override bool IsAsync => _impl.IsAsync; + internal override bool IsAsync => _strategy.IsAsync; - internal override string Name => _impl.Name; + internal override string Name => _strategy.Name; - internal override IntPtr Handle => _impl.Handle; + internal override IntPtr Handle => _strategy.Handle; - internal override SafeFileHandle SafeFileHandle => _impl.SafeFileHandle; + internal override SafeFileHandle SafeFileHandle => _strategy.SafeFileHandle; - internal override bool IsClosed => _impl.IsClosed; + internal override bool IsClosed => _strategy.IsClosed; - internal override void Lock(long position, long length) => _impl.Lock(position, length); + internal override void Lock(long position, long length) => _strategy.Lock(position, length); - internal override void Unlock(long position, long length) => _impl.Unlock(position, length); + internal override void Unlock(long position, long length) => _strategy.Unlock(position, length); - public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); + public override long Seek(long offset, SeekOrigin origin) => _strategy.Seek(offset, origin); - public override void SetLength(long value) => _impl.SetLength(value); + public override void SetLength(long value) => _strategy.SetLength(value); - public override int ReadByte() => _impl.ReadByte(); + public override int ReadByte() => _strategy.ReadByte(); public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => _impl.IsAsync - ? _impl.BeginRead(buffer, offset, count, callback, state) + => _strategy.IsAsync + ? _strategy.BeginRead(buffer, offset, count, callback, state) : _fileStream.BaseBeginRead(buffer, offset, count, callback, state); public override int EndRead(IAsyncResult asyncResult) - => _impl.IsAsync ? _impl.EndRead(asyncResult) : _fileStream.BaseEndRead(asyncResult); + => _strategy.IsAsync ? _strategy.EndRead(asyncResult) : _fileStream.BaseEndRead(asyncResult); - public override int Read(byte[] buffer, int offset, int count) => _impl.Read(buffer, offset, count); + public override int Read(byte[] buffer, int offset, int count) => _strategy.Read(buffer, offset, count); // If this is a derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) // overload being introduced. In that case, this Read(Span) overload should use the behavior @@ -81,15 +81,15 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken => _fileStream.BaseReadAsync(buffer, cancellationToken); public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => _impl.IsAsync - ? _impl.BeginWrite(buffer, offset, count, callback, state) + => _strategy.IsAsync + ? _strategy.BeginWrite(buffer, offset, count, callback, state) : _fileStream.BaseBeginWrite(buffer, offset, count, callback, state); public override void EndWrite(IAsyncResult asyncResult) { - if (_impl.IsAsync) + if (_strategy.IsAsync) { - _impl.EndWrite(asyncResult); + _strategy.EndWrite(asyncResult); } else { @@ -97,9 +97,9 @@ public override void EndWrite(IAsyncResult asyncResult) } } - public override void WriteByte(byte value) => _impl.WriteByte(value); + public override void WriteByte(byte value) => _strategy.WriteByte(value); - public override void Write(byte[] buffer, int offset, int count) => _impl.Write(buffer, offset, count); + public override void Write(byte[] buffer, int offset, int count) => _strategy.Write(buffer, offset, count); // If this is a derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior @@ -121,7 +121,7 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo public override void Flush() => throw new InvalidOperationException("FileStream should never call this method."); - internal override void Flush(bool flushToDisk) => _impl.Flush(flushToDisk); + internal override void Flush(bool flushToDisk) => _strategy.Flush(flushToDisk); // If we have been inherited into a subclass, the following implementation could be incorrect // since it does not call through to Flush() which a subclass might have overridden. To be safe @@ -138,6 +138,6 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio public override ValueTask DisposeAsync() => _fileStream.BaseDisposeAsync(); - internal override void DisposeInternal(bool disposing) => _impl.DisposeInternal(disposing); + internal override void DisposeInternal(bool disposing) => _strategy.DisposeInternal(disposing); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 8ec4944b04850..6bff2e102a2fd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -17,7 +17,7 @@ public class FileStream : Stream /// Caches whether Serialization Guard has been disabled for file writes private static int s_cachedSerializationSwitch; - private readonly FileStreamStrategy _impl; + private readonly FileStreamStrategy _strategy; [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access) @@ -49,10 +49,10 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS switch (isAsync) { case true: - _impl = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, true)); + _strategy = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, true)); return; case false: - _impl = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, false)); + _strategy = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, false)); return; } } @@ -87,7 +87,7 @@ private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int } private FileStreamStrategy WrapForDerivedType(FileStreamStrategy impl) - => GetType() == typeof(FileStream) ? impl : new DerivedFileStreamImpl(this, impl); + => GetType() == typeof(FileStream) ? impl : new DerivedFileStreamStrategy(this, impl); public FileStream(SafeFileHandle handle, FileAccess access) : this(handle, access, DefaultBufferSize) @@ -106,10 +106,10 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool switch (isAsync) { case true: - _impl = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, true)); + _strategy = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, true)); return; case false: - _impl = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, false)); + _strategy = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, false)); return; } } @@ -180,11 +180,11 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } - _impl = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); + _strategy = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle => _impl.Handle; + public virtual IntPtr Handle => _strategy.Handle; public virtual void Lock(long position, long length) { @@ -193,12 +193,12 @@ public virtual void Lock(long position, long length) throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); } - if (_impl.IsClosed) + if (_strategy.IsClosed) { throw Error.GetFileNotOpen(); } - _impl.Lock(position, length); + _strategy.Lock(position, length); } public virtual void Unlock(long position, long length) @@ -208,12 +208,12 @@ public virtual void Unlock(long position, long length) throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); } - if (_impl.IsClosed) + if (_strategy.IsClosed) { throw Error.GetFileNotOpen(); } - _impl.Unlock(position, length); + _strategy.Unlock(position, length); } public override Task FlushAsync(CancellationToken cancellationToken) @@ -222,12 +222,12 @@ public override Task FlushAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - if (_impl.IsClosed) + if (_strategy.IsClosed) { throw Error.GetFileNotOpen(); } - return _impl.FlushAsync(cancellationToken); + return _strategy.FlushAsync(cancellationToken); } internal Task BaseFlushAsync(CancellationToken cancellationToken) @@ -237,10 +237,10 @@ public override int Read(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); - return _impl.Read(buffer, offset, count); + return _strategy.Read(buffer, offset, count); } - public override int Read(Span buffer) => _impl.Read(buffer); + public override int Read(Span buffer) => _strategy.Read(buffer); internal int BaseRead(Span buffer) => base.Read(buffer); @@ -251,10 +251,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); - if (_impl.IsClosed) + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - return _impl.ReadAsync(buffer, offset, count, cancellationToken); + return _strategy.ReadAsync(buffer, offset, count, cancellationToken); } internal Task BaseReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -267,12 +267,12 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken return ValueTask.FromCanceled(cancellationToken); } - if (_impl.IsClosed) + if (_strategy.IsClosed) { throw Error.GetFileNotOpen(); } - return _impl.ReadAsync(buffer, cancellationToken); + return _strategy.ReadAsync(buffer, cancellationToken); } internal ValueTask BaseReadAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -282,10 +282,10 @@ public override void Write(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); - _impl.Write(buffer, offset, count); + _strategy.Write(buffer, offset, count); } - public override void Write(ReadOnlySpan buffer) => _impl.Write(buffer); + public override void Write(ReadOnlySpan buffer) => _strategy.Write(buffer); internal void BaseWrite(ReadOnlySpan buffer) => base.Write(buffer); @@ -296,10 +296,10 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); - if (_impl.IsClosed) + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - return _impl.WriteAsync(buffer, offset, count, cancellationToken); + return _strategy.WriteAsync(buffer, offset, count, cancellationToken); } internal Task BaseWriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -312,12 +312,12 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return ValueTask.FromCanceled(cancellationToken); } - if (_impl.IsClosed) + if (_strategy.IsClosed) { throw Error.GetFileNotOpen(); } - return _impl.WriteAsync(buffer, cancellationToken); + return _strategy.WriteAsync(buffer, cancellationToken); } internal ValueTask BaseWriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) @@ -338,16 +338,16 @@ public override void Flush() /// public virtual void Flush(bool flushToDisk) { - if (_impl.IsClosed) throw Error.GetFileNotOpen(); + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - _impl.Flush(flushToDisk); + _strategy.Flush(flushToDisk); } /// Gets a value indicating whether the current stream supports reading. - public override bool CanRead => _impl.CanRead; + public override bool CanRead => _strategy.CanRead; /// Gets a value indicating whether the current stream supports writing. - public override bool CanWrite => _impl.CanWrite; + public override bool CanWrite => _strategy.CanWrite; /// Validates arguments to Read and Write and throws resulting exceptions. /// The buffer to read from or write to. @@ -356,7 +356,7 @@ public virtual void Flush(bool flushToDisk) private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); - if (_impl.IsClosed) + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); } @@ -366,32 +366,32 @@ public override void SetLength(long value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - if (_impl.IsClosed) + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - if (!_impl.CanSeek) + if (!_strategy.CanSeek) throw Error.GetSeekNotSupported(); - if (!_impl.CanWrite) + if (!_strategy.CanWrite) throw Error.GetWriteNotSupported(); - _impl.SetLength(value); + _strategy.SetLength(value); } - public virtual SafeFileHandle SafeFileHandle => _impl.SafeFileHandle; + public virtual SafeFileHandle SafeFileHandle => _strategy.SafeFileHandle; /// Gets the path that was passed to the constructor. - public virtual string Name => _impl.Name; + public virtual string Name => _strategy.Name; /// Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously. - public virtual bool IsAsync => _impl.IsAsync; + public virtual bool IsAsync => _strategy.IsAsync; /// Gets the length of the stream in bytes. public override long Length { get { - if (_impl.IsClosed) throw Error.GetFileNotOpen(); - if (!_impl.CanSeek) throw Error.GetSeekNotSupported(); - return _impl.Length; + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); + if (!_strategy.CanSeek) throw Error.GetSeekNotSupported(); + return _strategy.Length; } } @@ -400,37 +400,37 @@ public override long Position { get { - if (_impl.IsClosed) + if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - if (!_impl.CanSeek) + if (!_strategy.CanSeek) throw Error.GetSeekNotSupported(); - return _impl.Position; + return _strategy.Position; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - _impl.Seek(value, SeekOrigin.Begin); + _strategy.Seek(value, SeekOrigin.Begin); } } - internal virtual bool IsClosed => _impl.IsClosed; + internal virtual bool IsClosed => _strategy.IsClosed; /// /// Reads a byte from the file stream. Returns the byte cast to an int /// or -1 if reading from the end of the stream. /// - public override int ReadByte() => _impl.ReadByte(); + public override int ReadByte() => _strategy.ReadByte(); /// /// Writes a byte to the current position in the stream and advances the position /// within the stream by one byte. /// /// The byte to write to the stream. - public override void WriteByte(byte value) => _impl.WriteByte(value); + public override void WriteByte(byte value) => _strategy.WriteByte(value); ~FileStream() { @@ -442,18 +442,18 @@ public override long Position protected override void Dispose(bool disposing) { - if (_impl != null) // possible in finalizer + if (_strategy != null) // possible in finalizer { - _impl.DisposeInternal(disposing); + _strategy.DisposeInternal(disposing); } } - public override ValueTask DisposeAsync() => _impl.DisposeAsync(); + public override ValueTask DisposeAsync() => _strategy.DisposeAsync(); internal ValueTask BaseDisposeAsync() => base.DisposeAsync(); public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - => _impl.CopyToAsync(destination, bufferSize, cancellationToken); + => _strategy.CopyToAsync(destination, bufferSize, cancellationToken); internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => base.CopyToAsync(destination, bufferSize, cancellationToken); @@ -461,10 +461,10 @@ internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationTo public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (_impl.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!_impl.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + if (_strategy.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!_strategy.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - return _impl.BeginRead(buffer, offset, count, callback, state); + return _strategy.BeginRead(buffer, offset, count, callback, state); } internal IAsyncResult BaseBeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) @@ -475,7 +475,7 @@ public override int EndRead(IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - return _impl.EndRead(asyncResult); + return _strategy.EndRead(asyncResult); } internal int BaseEndRead(IAsyncResult asyncResult) => base.EndRead(asyncResult); @@ -483,10 +483,10 @@ public override int EndRead(IAsyncResult asyncResult) public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (_impl.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!_impl.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + if (_strategy.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!_strategy.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - return _impl.BeginWrite(buffer, offset, count, callback, state); + return _strategy.BeginWrite(buffer, offset, count, callback, state); } internal IAsyncResult BaseBeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) @@ -497,13 +497,13 @@ public override void EndWrite(IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - _impl.EndWrite(asyncResult); + _strategy.EndWrite(asyncResult); } internal void BaseEndWrite(IAsyncResult asyncResult) => base.EndWrite(asyncResult); - public override bool CanSeek => _impl.CanSeek; + public override bool CanSeek => _strategy.CanSeek; - public override long Seek(long offset, SeekOrigin origin) => _impl.Seek(offset, origin); + public override long Seek(long offset, SeekOrigin origin) => _strategy.Seek(offset, origin); } } From 227d98ddb4ddcb41366685a58307a6a97051fb9e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:01:39 +0100 Subject: [PATCH 057/100] remove .Handle from the Strategy --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 6bff2e102a2fd..76404557e3863 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -184,7 +184,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle => _strategy.Handle; + public virtual IntPtr Handle => _strategy.SafeFileHandle.DangerousGetHandle(); // TODO: is it OK that this method has some logic? public virtual void Lock(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs index 33e9a7512b99f..f4657da1495b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs @@ -15,8 +15,6 @@ internal abstract class FileStreamStrategy : Stream internal abstract string Name { get; } - internal abstract IntPtr Handle { get; } - internal abstract SafeFileHandle SafeFileHandle { get; } internal abstract bool IsClosed { get; } From 8c865a8e051c59ecb33f24282a965385c3122fa3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:06:44 +0100 Subject: [PATCH 058/100] rename FileStreamImpl => CommonFileStreamStrategyTemplate --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../{FileStreamImpl.cs => CommonFileStreamStrategyTemplate.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamImpl.cs => CommonFileStreamStrategyTemplate.cs} (99%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 15a9875cd11cd..85f961b69ce90 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -397,7 +397,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index b5cc30ba9449e..6fbae930a69ec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal abstract class CommonFileStreamStrategyTemplate : FileStreamStrategy { private byte[]? _buffer; private readonly int _bufferLength; From e82a23582dc5f829d2b3f6d5c0f9f78fe424d685 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:09:57 +0100 Subject: [PATCH 059/100] rename FileStreamImpl.Unix => UnixFileStreamStrategy --- .../src/System.Private.CoreLib.Shared.projitems | 6 +++--- ...mImpl.Lock.OSX.cs => UnixFileStreamStrategy.Lock.OSX.cs} | 2 +- ...mpl.Lock.Unix.cs => UnixFileStreamStrategy.Lock.Unix.cs} | 2 +- .../{FileStreamImpl.Unix.cs => UnixFileStreamStrategy.cs} | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamImpl.Lock.OSX.cs => UnixFileStreamStrategy.Lock.OSX.cs} (85%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamImpl.Lock.Unix.cs => UnixFileStreamStrategy.Lock.Unix.cs} (92%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamImpl.Unix.cs => UnixFileStreamStrategy.cs} (99%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 85f961b69ce90..3a1da65e2e36a 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1819,9 +1819,9 @@ - - - + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs similarity index 85% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs index 7330a5147d988..17b4dfbaaa5b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate { private static void LockInternal(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs similarity index 92% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs index ab4fc563020b3..fd17db787ea84 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 51578bbfa104c..cb8239c5e1695 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate { /// File mode. private FileMode _mode; From 87568210b2f5c4ef405c5ed0265d3b4b3857ea31 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:12:46 +0100 Subject: [PATCH 060/100] rename FileStreamImpl.Windows => WindowsFileStreamStrategy --- .../src/System.Private.CoreLib.Shared.projitems | 4 ++-- ...StreamImpl.Win32.cs => WindowsFileStreamStrategy.Win32.cs} | 2 +- ...FileStreamImpl.Windows.cs => WindowsFileStreamStrategy.cs} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamImpl.Win32.cs => WindowsFileStreamStrategy.Win32.cs} (97%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamImpl.Windows.cs => WindowsFileStreamStrategy.cs} (99%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3a1da65e2e36a..d0bc669ee7531 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1614,8 +1614,8 @@ - - + + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs similarity index 97% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs index 836569a6b3920..c47e7dd54fac0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs @@ -6,7 +6,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index b9615cf9f60a0..9aebad71b8e2b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamImpl.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -39,7 +39,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. From d7dd225f4d31b9e2d273a6ade39b352dc82e2ac8 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:27:13 +0100 Subject: [PATCH 061/100] make the fields protected so they can be accessed from derived types --- .../IO/CommonFileStreamStrategyTemplate.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 6fbae930a69ec..3151868191d81 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -11,24 +11,24 @@ namespace System.IO { internal abstract class CommonFileStreamStrategyTemplate : FileStreamStrategy { - private byte[]? _buffer; - private readonly int _bufferLength; - private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws + protected byte[]? _buffer; + protected readonly int _bufferLength; + protected readonly SafeFileHandle _fileHandle; // only ever null if ctor throws /// Whether the file is opened for reading, writing, or both. - private readonly FileAccess _access; + protected readonly FileAccess _access; /// The path to the opened file. - private readonly string? _path; + protected readonly string? _path; /// The next available byte to be read from the _buffer. - private int _readPos; + protected int _readPos; /// The number of valid bytes in _buffer. - private int _readLength; + protected int _readLength; /// The next location in which a write should occur to the buffer. - private int _writePos; + protected int _writePos; /// /// Whether asynchronous read/write/flush operations should be performed using async I/O. @@ -45,20 +45,20 @@ internal abstract class CommonFileStreamStrategyTemplate : FileStreamStrategy /// delegate to the base stream, and no attempt is made to synchronize. If async, we use /// a semaphore to coordinate both sync and async operations. /// - private readonly bool _useAsyncIO; + protected readonly bool _useAsyncIO; /// cached task for read ops that complete synchronously - private Task? _lastSynchronouslyCompletedTask; + protected Task? _lastSynchronouslyCompletedTask; /// /// Currently cached position in the stream. This should always mirror the underlying file's actual position, /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which /// point we attempt to error out. /// - private long _filePosition; + protected long _filePosition; /// Whether the file stream's handle has been exposed. - private bool _exposedHandle; + protected bool _exposedHandle; internal FileStreamImpl(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) { From 23446ee6cfb5b766297d8fb8fc8b57fa53bd7023 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:27:49 +0100 Subject: [PATCH 062/100] remove handle --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 3151868191d81..dc598266ad73f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -113,7 +113,6 @@ internal FileStreamImpl(FileStream fileStream, string path, FileMode mode, FileA internal override void DisposeInternal(bool disposing) => Dispose(disposing); - internal override IntPtr Handle => SafeFileHandle.DangerousGetHandle(); internal override void Lock(long position, long length) => LockInternal(position, length); From 479371703c405a76ec36c4581ff871aa532b6ebd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:28:14 +0100 Subject: [PATCH 063/100] fix finalizer --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index dc598266ad73f..c7c4fe7f4f9f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -104,7 +104,7 @@ internal FileStreamImpl(FileStream fileStream, string path, FileMode mode, FileA } } - ~FileStreamImpl() + ~CommonFileStreamStrategyTemplate() { // it looks like having this finalizer is mandatory, // as we can not guarantee that the Strategy won't be null in FileStream finalizer From 6c79ac5800f18a0e7d277ada2dbf84c0bfe1c647 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:29:24 +0100 Subject: [PATCH 064/100] fix the ctors, make init methods abstract and part of the template --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 11 ++++++++--- .../src/System/IO/UnixFileStreamStrategy.cs | 6 +++--- .../src/System/IO/WindowsFileStreamStrategy.Win32.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 4 ++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index c7c4fe7f4f9f4..09690e580176d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -60,7 +60,7 @@ internal abstract class CommonFileStreamStrategyTemplate : FileStreamStrategy /// Whether the file stream's handle has been exposed. protected bool _exposedHandle; - internal FileStreamImpl(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) + protected CommonFileStreamStrategyTemplate(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) { _exposedHandle = true; _bufferLength = bufferSize; @@ -77,7 +77,7 @@ internal FileStreamImpl(FileStream fileStream, SafeFileHandle handle, FileAccess _fileHandle = handle; } - internal FileStreamImpl(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) + protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) { string fullPath = Path.GetFullPath(path); @@ -111,8 +111,13 @@ internal FileStreamImpl(FileStream fileStream, string path, FileMode mode, FileA Dispose(false); } - internal override void DisposeInternal(bool disposing) => Dispose(disposing); + protected abstract void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO); + + protected abstract SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options); + protected abstract void Init(FileMode mode, FileShare share, string originalPath); + + internal override void DisposeInternal(bool disposing) => Dispose(disposing); internal override void Lock(long position, long length) => LockInternal(position, length); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index cb8239c5e1695..ac4c0be907715 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -34,7 +34,7 @@ internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyT /// Lazily-initialized value for whether the file supports seeking. private bool? _canSeek; - private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + protected sealed override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) @@ -67,7 +67,7 @@ private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions op /// How the file should be opened. /// What other access to the file should be allowed. This is currently ignored. /// The original path specified for the FileStream. - private void Init(FileMode mode, FileShare share, string originalPath) + protected sealed override void Init(FileMode mode, FileShare share, string originalPath) { _fileHandle.IsAsync = _useAsyncIO; @@ -125,7 +125,7 @@ private void Init(FileMode mode, FileShare share, string originalPath) } /// Initializes a stream from an already open file handle (file descriptor). - private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + protected sealed override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { if (useAsyncIO) _asyncState = new AsyncState(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs index c47e7dd54fac0..a465ebe2f5e62 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs @@ -8,7 +8,7 @@ namespace System.IO { internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate { - private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + protected sealed override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { return CreateFileOpenHandle(mode, share, options); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 9aebad71b8e2b..a3ff75494002e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -51,7 +51,7 @@ internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrate private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped - private void Init(FileMode mode, FileShare share, string originalPath) + protected sealed override void Init(FileMode mode, FileShare share, string originalPath) { if (!PathInternal.IsExtended(originalPath)) { @@ -118,7 +118,7 @@ private void Init(FileMode mode, FileShare share, string originalPath) } } - private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + protected sealed override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { #if DEBUG bool hadBinding = handle.ThreadPoolBinding != null; From cc656781916c654d25d083befee52b280c1f5f27 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:36:25 +0100 Subject: [PATCH 065/100] Lock & Unlock --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 4 ---- .../src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs | 4 ++-- .../src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs | 4 ++-- .../src/System/IO/WindowsFileStreamStrategy.cs | 4 ++-- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 09690e580176d..3af78962518bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -119,10 +119,6 @@ protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, F internal override void DisposeInternal(bool disposing) => Dispose(disposing); - internal override void Lock(long position, long length) => LockInternal(position, length); - - internal override void Unlock(long position, long length) => UnlockInternal(position, length); - public override Task FlushAsync(CancellationToken cancellationToken) { // TODO: https://github.com/dotnet/runtime/issues/27643 (stop doing this synchronous work!!). diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs index 17b4dfbaaa5b6..3d85797dca3a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs @@ -5,12 +5,12 @@ namespace System.IO { internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate { - private static void LockInternal(long position, long length) + internal override void Lock(long position, long length) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); } - private static void UnlockInternal(long position, long length) + internal override void Unlock(long position, long length) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs index fd17db787ea84..6a7a174548774 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs @@ -8,7 +8,7 @@ internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyT /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. /// The range to be locked. - private void LockInternal(long position, long length) + internal override void Lock(long position, long length) { CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, CanWrite ? Interop.Sys.LockType.F_WRLCK : Interop.Sys.LockType.F_RDLCK)); } @@ -16,7 +16,7 @@ private void LockInternal(long position, long length) /// Allows access by other processes to all or part of a file that was previously locked. /// The beginning of the range to unlock. /// The range to be unlocked. - private void UnlockInternal(long position, long length) + internal override void Unlock(long position, long length) { CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_UNLCK)); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index a3ff75494002e..f07039c0e1c06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -1540,7 +1540,7 @@ public void UnsafeOnCompleted(Action continuation) } } - private void LockInternal(long position, long length) + internal override void Lock(long position, long length) { int positionLow = unchecked((int)(position)); int positionHigh = unchecked((int)(position >> 32)); @@ -1553,7 +1553,7 @@ private void LockInternal(long position, long length) } } - private void UnlockInternal(long position, long length) + internal override void Unlock(long position, long length) { int positionLow = unchecked((int)(position)); int positionHigh = unchecked((int)(position >> 32)); From 6d2db24cb52b64b0948980cfecf62fa4423748d3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:38:29 +0100 Subject: [PATCH 066/100] FlushWriteBuffer --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 ++ .../src/System/IO/UnixFileStreamStrategy.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 3af78962518bc..7d2b27fc5f0c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -117,6 +117,8 @@ protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, F protected abstract void Init(FileMode mode, FileShare share, string originalPath); + protected abstract void FlushWriteBuffer(bool calledFromFinalizer = false); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index ac4c0be907715..3ea522a9efeff 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -342,7 +342,7 @@ private void FlushWriteBufferForWriteByte() } /// Writes any data in the write buffer to the underlying stream and resets the buffer. - private void FlushWriteBuffer() + protected sealed override void FlushWriteBuffer(bool calledFromFinalizer = false) { AssertBufferInvariants(); if (_writePos > 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index f07039c0e1c06..25d3bc6179863 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -319,7 +319,7 @@ private Task FlushWriteAsync(CancellationToken cancellationToken) // Writes are buffered. Anytime the buffer fills up // (_writePos + delta > _bufferSize) or the buffer switches to reading // and there is left over data (_writePos > 0), this function must be called. - private void FlushWriteBuffer(bool calledFromFinalizer = false) + protected sealed override void FlushWriteBuffer(bool calledFromFinalizer = false) { if (_writePos == 0) return; Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!"); From 01899df15fbdd463fd8bc6e4037d9930b39a86a4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:41:34 +0100 Subject: [PATCH 067/100] FlushOSBuffer --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 ++ .../src/System/IO/UnixFileStreamStrategy.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 7d2b27fc5f0c5..c284b9b63092b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -119,6 +119,8 @@ protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, F protected abstract void FlushWriteBuffer(bool calledFromFinalizer = false); + protected abstract void FlushOSBuffer(); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 3ea522a9efeff..845fb19a9deaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -313,7 +313,7 @@ public override ValueTask DisposeAsync() } /// Flushes the OS buffer. This does not flush the internal read/write buffer. - private void FlushOSBuffer() + protected override void FlushOSBuffer() { if (Interop.Sys.FSync(_fileHandle) < 0) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 25d3bc6179863..06d0d6a1033ba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -286,7 +286,7 @@ public override async ValueTask DisposeAsync() } } - private void FlushOSBuffer() + protected override void FlushOSBuffer() { if (!Interop.Kernel32.FlushFileBuffers(_fileHandle)) { From c2cc4113ba7e9ac51bce49a07199e9d1158c732b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:45:07 +0100 Subject: [PATCH 068/100] there is no need to have selead methods in a sealed type... --- .../src/System/IO/UnixFileStreamStrategy.cs | 8 ++++---- .../src/System/IO/WindowsFileStreamStrategy.Win32.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 845fb19a9deaa..78923a1af2c02 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -34,7 +34,7 @@ internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyT /// Lazily-initialized value for whether the file supports seeking. private bool? _canSeek; - protected sealed override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) @@ -67,7 +67,7 @@ protected sealed override SafeFileHandle OpenHandle(FileMode mode, FileShare sha /// How the file should be opened. /// What other access to the file should be allowed. This is currently ignored. /// The original path specified for the FileStream. - protected sealed override void Init(FileMode mode, FileShare share, string originalPath) + protected override void Init(FileMode mode, FileShare share, string originalPath) { _fileHandle.IsAsync = _useAsyncIO; @@ -125,7 +125,7 @@ protected sealed override void Init(FileMode mode, FileShare share, string origi } /// Initializes a stream from an already open file handle (file descriptor). - protected sealed override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + protected override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { if (useAsyncIO) _asyncState = new AsyncState(); @@ -342,7 +342,7 @@ private void FlushWriteBufferForWriteByte() } /// Writes any data in the write buffer to the underlying stream and resets the buffer. - protected sealed override void FlushWriteBuffer(bool calledFromFinalizer = false) + protected override void FlushWriteBuffer(bool calledFromFinalizer = false) { AssertBufferInvariants(); if (_writePos > 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs index a465ebe2f5e62..eef7f293185f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs @@ -8,7 +8,7 @@ namespace System.IO { internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate { - protected sealed override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { return CreateFileOpenHandle(mode, share, options); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 06d0d6a1033ba..83d5fc7e5605e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -51,7 +51,7 @@ internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrate private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped - protected sealed override void Init(FileMode mode, FileShare share, string originalPath) + protected override void Init(FileMode mode, FileShare share, string originalPath) { if (!PathInternal.IsExtended(originalPath)) { @@ -118,7 +118,7 @@ protected sealed override void Init(FileMode mode, FileShare share, string origi } } - protected sealed override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + protected override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { #if DEBUG bool hadBinding = handle.ThreadPoolBinding != null; @@ -319,7 +319,7 @@ private Task FlushWriteAsync(CancellationToken cancellationToken) // Writes are buffered. Anytime the buffer fills up // (_writePos + delta > _bufferSize) or the buffer switches to reading // and there is left over data (_writePos > 0), this function must be called. - protected sealed override void FlushWriteBuffer(bool calledFromFinalizer = false) + protected override void FlushWriteBuffer(bool calledFromFinalizer = false) { if (_writePos == 0) return; Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!"); From a92424d768d9a8def3a9fdf8e046a8142b5984fd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:45:41 +0100 Subject: [PATCH 069/100] SetLength --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 -- .../src/System/IO/UnixFileStreamStrategy.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index c284b9b63092b..eb24c88a3fab5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -298,8 +298,6 @@ internal override void Flush(bool flushToDisk) public override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; - public override void SetLength(long value) => SetLengthInternal(value); - internal override SafeFileHandle SafeFileHandle { get diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 78923a1af2c02..02e3d7d94329e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -354,7 +354,7 @@ protected override void FlushWriteBuffer(bool calledFromFinalizer = false) /// Sets the length of this stream to the given value. /// The new length of the stream. - private void SetLengthInternal(long value) + public override void SetLength(long value) { FlushInternalBuffer(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 83d5fc7e5605e..a7919884aed38 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -353,7 +353,7 @@ protected override void FlushWriteBuffer(bool calledFromFinalizer = false) _writePos = 0; } - private void SetLengthInternal(long value) + public override void SetLength(long value) { // Handle buffering updates. if (_writePos > 0) From e360787aa011f568d1bf9602f3f393a251bc64f7 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:47:25 +0100 Subject: [PATCH 070/100] Length {get;} --- .../IO/CommonFileStreamStrategyTemplate.cs | 2 -- .../src/System/IO/UnixFileStreamStrategy.cs | 27 ++++++++++--------- .../System/IO/WindowsFileStreamStrategy.cs | 25 +++++++++-------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index eb24c88a3fab5..6049f1dafa815 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -312,8 +312,6 @@ internal override SafeFileHandle SafeFileHandle internal override bool IsAsync => _useAsyncIO; - public override long Length => GetLengthInternal(); - /// /// Verify that the actual position of the OS's handle equals what we expect it to. /// This will fail if someone else moved the UnixFileStream's handle or if diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 02e3d7d94329e..a71a3cde882c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -223,21 +223,24 @@ private bool CanSeekCore(SafeFileHandle fileHandle) return _canSeek.GetValueOrDefault(); } - private long GetLengthInternal() + public override long Length { - // Get the length of the file as reported by the OS - Interop.Sys.FileStatus status; - CheckFileCall(Interop.Sys.FStat(_fileHandle, out status)); - long length = status.Size; - - // But we may have buffered some data to be written that puts our length - // beyond what the OS is aware of. Update accordingly. - if (_writePos > 0 && _filePosition + _writePos > length) + get { - length = _writePos + _filePosition; - } + // Get the length of the file as reported by the OS + Interop.Sys.FileStatus status; + CheckFileCall(Interop.Sys.FStat(_fileHandle, out status)); + long length = status.Size; + + // But we may have buffered some data to be written that puts our length + // beyond what the OS is aware of. Update accordingly. + if (_writePos > 0 && _filePosition + _writePos > length) + { + length = _writePos + _filePosition; + } - return length; + return length; + } } /// Releases the unmanaged resources used by the stream. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index a7919884aed38..6f4c01762f0a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -199,21 +199,24 @@ private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare public override bool CanSeek => _canSeek; - private unsafe long GetLengthInternal() + public unsafe override long Length { - Interop.Kernel32.FILE_STANDARD_INFO info; + get + { + Interop.Kernel32.FILE_STANDARD_INFO info; - if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) - throw Win32Marshal.GetExceptionForLastWin32Error(_path); - long len = info.EndOfFile; + if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) + throw Win32Marshal.GetExceptionForLastWin32Error(_path); + long len = info.EndOfFile; - // If we're writing near the end of the file, we must include our - // internal buffer in our Length calculation. Don't flush because - // we use the length of the file in our async write method. - if (_writePos > 0 && _filePosition + _writePos > len) - len = _writePos + _filePosition; + // If we're writing near the end of the file, we must include our + // internal buffer in our Length calculation. Don't flush because + // we use the length of the file in our async write method. + if (_writePos > 0 && _filePosition + _writePos > len) + len = _writePos + _filePosition; - return len; + return len; + } } protected override void Dispose(bool disposing) From 3791eccc7209440575618cdaefd9bd3f18017a18 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:49:43 +0100 Subject: [PATCH 071/100] OnBufferAllocated --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 6 ++++-- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 6049f1dafa815..65a7e26f8e8ed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -121,6 +121,10 @@ protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, F protected abstract void FlushOSBuffer(); + protected virtual void OnBufferAllocated() + { + } + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) @@ -425,8 +429,6 @@ private byte[] GetBuffer() return _buffer; } - partial void OnBufferAllocated(); - /// /// Flushes the internal read/write buffer for this stream. If write data has been buffered, /// that data is written out to the underlying file. Or if data has been buffered for diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 6f4c01762f0a1..97d40dc41b406 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -626,7 +626,7 @@ private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, return ret; } - partial void OnBufferAllocated() + protected override void OnBufferAllocated() { Debug.Assert(_buffer != null); Debug.Assert(_preallocatedOverlapped == null); From c1a418a951027f071044cdb682562aa1d1945c14 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:51:43 +0100 Subject: [PATCH 072/100] FillReadBufferForReadByte --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 ++ .../src/System/IO/UnixFileStreamStrategy.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 65a7e26f8e8ed..424e9ac10dced 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -125,6 +125,8 @@ protected virtual void OnBufferAllocated() { } + protected abstract int FillReadBufferForReadByte(); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index a71a3cde882c5..d2e197b7aab05 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -546,7 +546,7 @@ private unsafe int ReadNative(Span buffer) } /// Reads from the file handle into the buffer, overwriting anything in it. - private int FillReadBufferForReadByte() + protected override int FillReadBufferForReadByte() { #pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 _asyncState?.Wait(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 97d40dc41b406..9444358086deb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -483,7 +483,7 @@ private void AssertCanRead() } /// Reads from the file handle into the buffer, overwriting anything in it. - private int FillReadBufferForReadByte() => + protected override int FillReadBufferForReadByte() => _useAsyncIO ? ReadNativeAsync(new Memory(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() : ReadNative(_buffer); From dab77a3e161400af602fca484fbeb4e772e3bb63 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:53:27 +0100 Subject: [PATCH 073/100] FlushWriteBufferForWriteByte --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 ++ .../src/System/IO/UnixFileStreamStrategy.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 424e9ac10dced..f5e0cf981b270 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -127,6 +127,8 @@ protected virtual void OnBufferAllocated() protected abstract int FillReadBufferForReadByte(); + protected abstract void FlushWriteBufferForWriteByte(); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index d2e197b7aab05..015db5050e0c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -335,7 +335,7 @@ protected override void FlushOSBuffer() } } - private void FlushWriteBufferForWriteByte() + protected override void FlushWriteBufferForWriteByte() { #pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 _asyncState?.Wait(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 9444358086deb..e6b245f6e0226 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -317,7 +317,7 @@ private Task FlushWriteAsync(CancellationToken cancellationToken) return flushTask; } - private void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); + protected override void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); // Writes are buffered. Anytime the buffer fills up // (_writePos + delta > _bufferSize) or the buffer switches to reading From 6e8d201ad05f082586cc9201cb407377ce2d8ca9 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 13:59:02 +0100 Subject: [PATCH 074/100] ReadSpan WriteSpan --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 4 ++++ .../src/System/IO/UnixFileStreamStrategy.cs | 4 ++-- .../src/System/IO/WindowsFileStreamStrategy.cs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index f5e0cf981b270..643da56e125ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -129,6 +129,10 @@ protected virtual void OnBufferAllocated() protected abstract void FlushWriteBufferForWriteByte(); + protected abstract int ReadSpan(Span destination); + + protected abstract void WriteSpan(ReadOnlySpan source); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 015db5050e0c0..334e860302db6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -378,7 +378,7 @@ public override void SetLength(long value) } /// Reads a block of bytes from the stream and writes the data in a given buffer. - private int ReadSpan(Span destination) + protected override int ReadSpan(Span destination) { PrepareForReading(); @@ -557,7 +557,7 @@ protected override int FillReadBufferForReadByte() /// Writes a block of bytes to the file stream. /// The buffer containing data to write to the stream. - private void WriteSpan(ReadOnlySpan source) + protected override void WriteSpan(ReadOnlySpan source) { PrepareForWriting(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index e6b245f6e0226..3f6438d12c487 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -410,7 +410,7 @@ private unsafe void SetLengthCore(long value) private FileStreamCompletionSource? CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); - private int ReadSpan(Span destination) + protected override int ReadSpan(Span destination) { Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), @@ -635,7 +635,7 @@ protected override void OnBufferAllocated() _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer); } - private void WriteSpan(ReadOnlySpan source) + protected override void WriteSpan(ReadOnlySpan source) { Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); From 08bbb0f10b698b1bf1870494d2ab492b734f1294 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:02:53 +0100 Subject: [PATCH 075/100] ReadAsyncInternal WriteAsyncInternal --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 4 ++++ .../src/System/IO/UnixFileStreamStrategy.cs | 4 ++-- .../src/System/IO/WindowsFileStreamStrategy.cs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 643da56e125ef..24cf99685a84e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -133,6 +133,10 @@ protected virtual void OnBufferAllocated() protected abstract void WriteSpan(ReadOnlySpan source); + protected abstract Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult); + + protected abstract ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 334e860302db6..f35c105097aec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -472,7 +472,7 @@ private unsafe int ReadNative(Span buffer) /// The token to monitor for cancellation requests. /// If the operation completes synchronously, the number of bytes read. /// A task that represents the asynchronous read operation. - private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) + protected override Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { Debug.Assert(_useAsyncIO); Debug.Assert(_asyncState != null); @@ -634,7 +634,7 @@ private unsafe void WriteNative(ReadOnlySpan source) /// The buffer to write data from. /// The token to monitor for cancellation requests. /// A task that represents the asynchronous write operation. - private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(_useAsyncIO); Debug.Assert(_asyncState != null); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 3f6438d12c487..15988c95ae6b7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -733,7 +733,7 @@ private unsafe void WriteCore(ReadOnlySpan source) return; } - private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) + protected override Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { Debug.Assert(_useAsyncIO); if (!CanRead) throw Error.GetReadNotSupported(); @@ -946,7 +946,7 @@ private unsafe Task ReadNativeAsync(Memory destination, int numBuffer return completionSource.Task; } - private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(_useAsyncIO); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); From 4e8603048ae4379cfdb46fd34c2d6d7ce866f1b3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:05:18 +0100 Subject: [PATCH 076/100] SeekCore --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 2 ++ .../src/System/IO/UnixFileStreamStrategy.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 24cf99685a84e..74bbe3e57d4af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -137,6 +137,8 @@ protected virtual void OnBufferAllocated() protected abstract ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken); + protected abstract long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false); + internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index f35c105097aec..d06e06a5991c3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -776,7 +776,7 @@ public override long Seek(long offset, SeekOrigin origin) /// point for offset, using a value of type SeekOrigin. /// /// The new position in the stream. - private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin) + protected override long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { Debug.Assert(!fileHandle.IsClosed && CanSeekCore(fileHandle)); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 15988c95ae6b7..be9bd05a50985 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -605,7 +605,7 @@ public override long Seek(long offset, SeekOrigin origin) // This doesn't do argument checking. Necessary for SetLength, which must // set the file pointer beyond the end of the file. This will update the // internal position - private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + protected override long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek"); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); From c85c3034c6ce7cf39c60ea741ffe3ae449433959 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:10:18 +0100 Subject: [PATCH 077/100] make the private helpers protected so they can be accessed from derived types --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index 74bbe3e57d4af..da23a42948c11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -335,7 +335,7 @@ internal override SafeFileHandle SafeFileHandle /// This will fail if someone else moved the UnixFileStream's handle or if /// our position updating code is incorrect. /// - private void VerifyOSHandlePosition() + protected void VerifyOSHandlePosition() { bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it #if DEBUG @@ -410,7 +410,7 @@ public override long Position internal override bool IsClosed => _fileHandle.IsClosed; - private static bool IsIoRelatedException(Exception e) => + protected static bool IsIoRelatedException(Exception e) => // These all derive from IOException // DirectoryNotFoundException // DriveNotFoundException @@ -431,7 +431,7 @@ e is NotSupportedException || /// If the array hasn't been allocated, this will lazily allocate it. /// /// The buffer. - private byte[] GetBuffer() + protected byte[] GetBuffer() { Debug.Assert(_buffer == null || _buffer.Length == _bufferLength); if (_buffer == null) @@ -463,7 +463,7 @@ private void FlushInternalBuffer() } /// Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary. - private void FlushReadBuffer() + protected void FlushReadBuffer() { // Reading is done by blocks from the file, but someone could read // 1 byte from the buffer then write. At that point, the OS's file From 5802b83471248fd10d6cd10e15f16851a48591b2 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:11:06 +0100 Subject: [PATCH 078/100] replaces usages of FileStreamImpl with appropriate Strategy --- .../System/IO/FileStreamCompletionSource.Win32.cs | 14 +++++++------- .../src/System/IO/UnixFileStreamStrategy.cs | 6 +++--- .../src/System/IO/WindowsFileStreamStrategy.cs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index 7e7a07a44255e..846ddd8e564c8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class FileStreamImpl : FileStreamStrategy + internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. @@ -25,7 +25,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource private static Action? s_cancelCallback; - private readonly FileStreamImpl _stream; + private readonly WindowsFileStreamStrategy _stream; private readonly int _numBufferedBytes; private CancellationTokenRegistration _cancellationRegistration; #if DEBUG @@ -35,7 +35,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource private long _result; // Using long since this needs to be used in Interlocked APIs // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - protected FileStreamCompletionSource(FileStreamImpl stream, int numBufferedBytes, byte[]? bytes) + protected FileStreamCompletionSource(WindowsFileStreamStrategy stream, int numBufferedBytes, byte[]? bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) { _numBufferedBytes = numBufferedBytes; @@ -132,8 +132,8 @@ internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated // overlapped was already in use by another operation). object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); - Debug.Assert(state is FileStreamImpl || state is FileStreamCompletionSource); - FileStreamCompletionSource completionSource = state is FileStreamImpl fs ? + Debug.Assert(state is WindowsFileStreamStrategy || state is FileStreamCompletionSource); + FileStreamCompletionSource completionSource = state is WindowsFileStreamStrategy fs ? fs._currentOverlappedOwner! : // must be owned (FileStreamCompletionSource)state!; Debug.Assert(completionSource != null); @@ -220,7 +220,7 @@ private static void Cancel(object? state) } } - public static FileStreamCompletionSource Create(FileStreamImpl stream, int numBufferedBytesRead, ReadOnlyMemory memory) + public static FileStreamCompletionSource Create(WindowsFileStreamStrategy stream, int numBufferedBytesRead, ReadOnlyMemory memory) { // If the memory passed in is the stream's internal buffer, we can use the base FileStreamCompletionSource, // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived @@ -241,7 +241,7 @@ private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSour { private MemoryHandle _handle; // mutable struct; do not make this readonly - internal MemoryFileStreamCompletionSource(FileStreamImpl stream, int numBufferedBytes, ReadOnlyMemory memory) : + internal MemoryFileStreamCompletionSource(WindowsFileStreamStrategy stream, int numBufferedBytes, ReadOnlyMemory memory) : base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes { _handle = memory.Pin(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index d06e06a5991c3..50e926902aa65 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -308,7 +308,7 @@ public override ValueTask DisposeAsync() // override may already exist on a derived type. if (_useAsyncIO && _writePos > 0) { - return new ValueTask(Task.Factory.StartNew(static s => ((FileStreamImpl)s!).Dispose(), this, + return new ValueTask(Task.Factory.StartNew(static s => ((UnixFileStreamStrategy)s!).Dispose(), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); } @@ -533,7 +533,7 @@ private unsafe int ReadNative(Span buffer) // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStreamImpl)s!; + var thisRef = (UnixFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -694,7 +694,7 @@ protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, Can // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStreamImpl)s!; + var thisRef = (UnixFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index be9bd05a50985..a5275b7701538 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -1470,7 +1470,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion internal static readonly IOCompletionCallback s_callback = IOCallback; /// The FileStream that owns this instance. - internal readonly FileStreamImpl _fileStream; + internal readonly WindowsFileStreamStrategy _fileStream; /// Tracked position representing the next location from which to read. internal long _position; @@ -1491,7 +1491,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion internal object CancellationLock => this; /// Initialize the awaitable. - internal AsyncCopyToAwaitable(FileStreamImpl fileStream) + internal AsyncCopyToAwaitable(WindowsFileStreamStrategy fileStream) { _fileStream = fileStream; } From 33f58df81fe9940b7aa41f444eb328b003aaa190 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:17:44 +0100 Subject: [PATCH 079/100] remove Handle from DerivedFileStreamStrategy --- .../src/System/IO/DerivedFileStreamStrategy.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs index d42ea000877d6..76d4441309f7b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs @@ -36,8 +36,6 @@ public override long Position internal override string Name => _strategy.Name; - internal override IntPtr Handle => _strategy.Handle; - internal override SafeFileHandle SafeFileHandle => _strategy.SafeFileHandle; internal override bool IsClosed => _strategy.IsClosed; From 2e20ab92a43da597c27d9bb80c50b3559f951c49 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:24:15 +0100 Subject: [PATCH 080/100] fix the ctors, it compiles on Windows! --- .../src/System/IO/FileStream.cs | 42 +++++++++---------- .../src/System/IO/UnixFileStreamStrategy.cs | 10 +++++ .../System/IO/WindowsFileStreamStrategy.cs | 10 +++++ 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 76404557e3863..107745108db85 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -45,16 +45,11 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS { ValidateHandle(safeHandle, access, bufferSize, isAsync); - // it might seem to have no sense now, but we plan to introduce dedicated strategies for sync and async implementations - switch (isAsync) - { - case true: - _strategy = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, true)); - return; - case false: - _strategy = WrapForDerivedType(new FileStreamImpl(this, safeHandle, access, bufferSize, false)); - return; - } +#if TARGET_WINDOWS + _strategy = WrapForDerivedType(new WindowsFileStreamStrategy(this, safeHandle, access, bufferSize, isAsync)); +#else + _strategy = WrapForDerivedType(new UnixFileStreamStrategy(this, safeHandle, access, bufferSize, isAsync)); +#endif } catch { @@ -95,7 +90,12 @@ public FileStream(SafeFileHandle handle, FileAccess access) } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, FileStreamImpl.GetDefaultIsAsync(handle, DefaultIsAsync)) + : this(handle, access, bufferSize, +#if TARGET_WINDOWS + WindowsFileStreamStrategy.GetDefaultIsAsync(handle, DefaultIsAsync)) +#else + UnixFileStreamStrategy.GetDefaultIsAsync(handle, DefaultIsAsync)) +#endif { } @@ -103,15 +103,11 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool { ValidateHandle(handle, access, bufferSize, isAsync); - switch (isAsync) - { - case true: - _strategy = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, true)); - return; - case false: - _strategy = WrapForDerivedType(new FileStreamImpl(this, handle, access, bufferSize, false)); - return; - } +#if TARGET_WINDOWS + _strategy = WrapForDerivedType(new WindowsFileStreamStrategy(this, handle, access, bufferSize, isAsync)); +#else + _strategy = WrapForDerivedType(new UnixFileStreamStrategy(this, handle, access, bufferSize, isAsync)); +#endif } public FileStream(string path, FileMode mode) : @@ -180,7 +176,11 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } - _strategy = WrapForDerivedType(new FileStreamImpl(this, path, mode, access, share, bufferSize, options)); +#if TARGET_WINDOWS + _strategy = WrapForDerivedType(new WindowsFileStreamStrategy(this, path, mode, access, share, bufferSize, options)); +#else + _strategy = WrapForDerivedType(new UnixFileStreamStrategy(this, path, mode, access, share, bufferSize, options)); +#endif } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 50e926902aa65..351817f17634f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -34,6 +34,16 @@ internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyT /// Lazily-initialized value for whether the file supports seeking. private bool? _canSeek; + internal UnixFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + : base(fileStream, handle, access, bufferSize, isAsync) + { + } + + internal UnixFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + : base(fileStream, path, mode, access, share, bufferSize, options) + { + } + protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index a5275b7701538..aa1db5c0d9c8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -51,6 +51,16 @@ internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrate private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped + internal WindowsFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + : base(fileStream, handle, access, bufferSize, isAsync) + { + } + + internal WindowsFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + : base(fileStream, path, mode, access, share, bufferSize, options) + { + } + protected override void Init(FileMode mode, FileShare share, string originalPath) { if (!PathInternal.IsExtended(originalPath)) From 1704b83906cf1d6d85b9283108ca363c7d932d0c Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 14:46:34 +0100 Subject: [PATCH 081/100] this is not needed anymore --- .../src/System/IO/UnixFileStreamStrategy.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 351817f17634f..62b12631db5fe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -716,11 +716,6 @@ protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, Can }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default)); } - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => - // Windows version overrides this method, so the Unix version does as well, but it doesn't - // currently have any special optimizations to be done and so just calls to the base. - base.CopyToAsync(destination, bufferSize, cancellationToken); - /// Sets the current position of this stream to the given value. /// The point relative to origin from which to begin seeking. /// From 34b7822383919c0c0e55ad7e3533679d75b198a6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 18:38:10 +0100 Subject: [PATCH 082/100] fix compilation errors for Unix --- .../src/System/IO/CommonFileStreamStrategyTemplate.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs index da23a42948c11..08e67bc4a95a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs @@ -362,7 +362,7 @@ protected void VerifyOSHandlePosition() /// Verifies that state relating to the read/write buffer is consistent. [Conditional("DEBUG")] - private void AssertBufferInvariants() + protected void AssertBufferInvariants() { // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength); @@ -375,7 +375,7 @@ private void AssertBufferInvariants() } /// Validates that we're ready to read from the stream. - private void PrepareForReading() + protected void PrepareForReading() { if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); @@ -449,7 +449,7 @@ protected byte[] GetBuffer() /// reading from the stream, the data is dumped and our position in the underlying file /// is rewound as necessary. This does not flush the OS buffer. /// - private void FlushInternalBuffer() + protected void FlushInternalBuffer() { AssertBufferInvariants(); if (_writePos > 0) @@ -526,7 +526,7 @@ public override void WriteByte(byte value) /// Validates that we're ready to write to the stream, /// including flushing a read buffer if necessary. /// - private void PrepareForWriting() + protected void PrepareForWriting() { if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); From 81cd5eb0c8d3f2a7f2f14c6861fb32130183fb18 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 22 Jan 2021 18:56:37 +0100 Subject: [PATCH 083/100] fix the build? --- .../src/System/IO/UnixFileStreamStrategy.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 62b12631db5fe..0a7d02774009a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -27,7 +27,7 @@ internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyT /// the semaphore used to serialize all operation, the buffer/offset/count provided by the /// caller for ReadAsync/WriteAsync operations, and the last successful task returned /// synchronously from ReadAsync which can be reused if the count matches the next request. - /// Only initialized when is true. + /// Only initialized when is true. /// private AsyncState? _asyncState; @@ -780,6 +780,7 @@ public override long Seek(long offset, SeekOrigin origin) /// Specifies the beginning, the end, or the current position as a reference /// point for offset, using a value of type SeekOrigin. /// + /// not used in Unix implementation /// The new position in the stream. protected override long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { From 0a4c55c4ef1470846ebf90e86b74b60150e201a2 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 25 Jan 2021 17:17:09 +0100 Subject: [PATCH 084/100] rename CommonFileStreamStrategyTemplate to FileStreamStrategyBase before everyone starts yelling at me --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../System/IO/FileStreamCompletionSource.Win32.cs | 2 +- ...StrategyTemplate.cs => FileStreamStrategyBase.cs} | 12 ++++++++---- .../src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs | 2 +- .../System/IO/UnixFileStreamStrategy.Lock.Unix.cs | 2 +- .../src/System/IO/UnixFileStreamStrategy.cs | 4 ++-- .../src/System/IO/WindowsFileStreamStrategy.Win32.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{CommonFileStreamStrategyTemplate.cs => FileStreamStrategyBase.cs} (96%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d0bc669ee7531..b6895d4bb41da 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -396,8 +396,8 @@ + - diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index 846ddd8e564c8..fb30f40a30f1b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate + internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs similarity index 96% rename from src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs index 08e67bc4a95a3..be01bc8d33ca3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/CommonFileStreamStrategyTemplate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs @@ -9,7 +9,11 @@ namespace System.IO { - internal abstract class CommonFileStreamStrategyTemplate : FileStreamStrategy + // This type exist so we can avoid code duplication between UnixFileStreamStrategy and WindowsFileStreamStrategy. + // As of now, it implements the Template pattern and defines multiple virtual methods, but in the future when we + // separate WindowsFileStreamStrategy into two separate strategies (Sync and Async), + // it should be just defining fields present in all implementations (and the non-virtual methods that use them) + internal abstract class FileStreamStrategyBase : FileStreamStrategy { protected byte[]? _buffer; protected readonly int _bufferLength; @@ -60,7 +64,7 @@ internal abstract class CommonFileStreamStrategyTemplate : FileStreamStrategy /// Whether the file stream's handle has been exposed. protected bool _exposedHandle; - protected CommonFileStreamStrategyTemplate(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) + protected FileStreamStrategyBase(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) { _exposedHandle = true; _bufferLength = bufferSize; @@ -77,7 +81,7 @@ protected CommonFileStreamStrategyTemplate(FileStream fileStream, SafeFileHandle _fileHandle = handle; } - protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) + protected FileStreamStrategyBase(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) { string fullPath = Path.GetFullPath(path); @@ -104,7 +108,7 @@ protected CommonFileStreamStrategyTemplate(FileStream fileStream, string path, F } } - ~CommonFileStreamStrategyTemplate() + ~FileStreamStrategyBase() { // it looks like having this finalizer is mandatory, // as we can not guarantee that the Strategy won't be null in FileStream finalizer diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs index 3d85797dca3a1..0e51b9c35ff91 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate + internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase { internal override void Lock(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs index 6a7a174548774..57e3c4b72de6f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate + internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 0a7d02774009a..41d7e2cf671b1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyTemplate + internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase { /// File mode. private FileMode _mode; @@ -27,7 +27,7 @@ internal sealed partial class UnixFileStreamStrategy : CommonFileStreamStrategyT /// the semaphore used to serialize all operation, the buffer/offset/count provided by the /// caller for ReadAsync/WriteAsync operations, and the last successful task returned /// synchronously from ReadAsync which can be reused if the count matches the next request. - /// Only initialized when is true. + /// Only initialized when is true. /// private AsyncState? _asyncState; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs index eef7f293185f1..6e218d8c3e3b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs @@ -6,7 +6,7 @@ namespace System.IO { - internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate + internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase { protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index aa1db5c0d9c8c..11dceec0d0532 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -39,7 +39,7 @@ namespace System.IO { - internal sealed partial class WindowsFileStreamStrategy : CommonFileStreamStrategyTemplate + internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. From 01f0cc30f4421938fb9608f61a75451e46d8d662 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 25 Jan 2021 17:49:49 +0100 Subject: [PATCH 085/100] introduce FileStreamStrategyHelper and move some of the parameterless static helper methods there --- .../System.Private.CoreLib.Shared.projitems | 5 +- .../src/System/IO/FileStream.cs | 7 +- .../src/System/IO/FileStreamStrategyBase.cs | 8 +- .../IO/FileStreamStrategyHelper.Unix.cs | 100 ++++++++++++++++++ ...cs => FileStreamStrategyHelper.Windows.cs} | 58 ++++++++-- .../src/System/IO/UnixFileStreamStrategy.cs | 94 +--------------- .../System/IO/WindowsFileStreamStrategy.cs | 39 +------ 7 files changed, 161 insertions(+), 150 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs rename src/libraries/System.Private.CoreLib/src/System/IO/{WindowsFileStreamStrategy.Win32.cs => FileStreamStrategyHelper.Windows.cs} (59%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index b6895d4bb41da..9c387b0ab38d7 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1615,7 +1615,7 @@ - + @@ -1819,9 +1819,10 @@ + - + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 107745108db85..8b31abc67cfb3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -90,12 +90,7 @@ public FileStream(SafeFileHandle handle, FileAccess access) } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, -#if TARGET_WINDOWS - WindowsFileStreamStrategy.GetDefaultIsAsync(handle, DefaultIsAsync)) -#else - UnixFileStreamStrategy.GetDefaultIsAsync(handle, DefaultIsAsync)) -#endif + : this(handle, access, bufferSize, FileStreamStrategyHelper.GetDefaultIsAsync(handle, DefaultIsAsync)) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs index be01bc8d33ca3..465d7dfded8ad 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs @@ -92,11 +92,11 @@ protected FileStreamStrategyBase(FileStream fileStream, string path, FileMode mo if ((options & FileOptions.Asynchronous) != 0) _useAsyncIO = true; - _fileHandle = OpenHandle(mode, share, options); + _fileHandle = FileStreamStrategyHelper.OpenHandle(fullPath, mode, access, share, options); try { - Init(mode, share, path); + Init(mode, share, path, options); } catch { @@ -117,9 +117,7 @@ protected FileStreamStrategyBase(FileStream fileStream, string path, FileMode mo protected abstract void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO); - protected abstract SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options); - - protected abstract void Init(FileMode mode, FileShare share, string originalPath); + protected abstract void Init(FileMode mode, FileShare share, string originalPath, FileOptions options); protected abstract void FlushWriteBuffer(bool calledFromFinalizer = false); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs new file mode 100644 index 0000000000000..4d313383a7524 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + // this type defines a set of stateless FileStreamStrategy helper methods + internal static class FileStreamStrategyHelper + { + internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + { + // Translate the arguments into arguments for an open call. + Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, access, share, options); + + // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and + // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out + // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the + // actual permissions will typically be less than what we select here. + const Interop.Sys.Permissions OpenPermissions = + Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR | + Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | + Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; + + // Open the file and store the safe handle. + return SafeFileHandle.Open(path!, openFlags, (int)OpenPermissions); + } + + internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsync) => handle.IsAsync ?? defaultIsAsync; + + /// Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file. + /// The FileMode provided to the stream's constructor. + /// The FileAccess provided to the stream's constructor + /// The FileShare provided to the stream's constructor + /// The FileOptions provided to the stream's constructor + /// The flags value to be passed to the open system call. + private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options) + { + // Translate FileMode. Most of the values map cleanly to one or more options for open. + Interop.Sys.OpenFlags flags = default; + switch (mode) + { + default: + case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. + case FileMode.Truncate: // We truncate the file after getting the lock + break; + + case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later + case FileMode.OpenOrCreate: + case FileMode.Create: // We truncate the file after getting the lock + flags |= Interop.Sys.OpenFlags.O_CREAT; + break; + + case FileMode.CreateNew: + flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); + break; + } + + // Translate FileAccess. All possible values map cleanly to corresponding values for open. + switch (access) + { + case FileAccess.Read: + flags |= Interop.Sys.OpenFlags.O_RDONLY; + break; + + case FileAccess.ReadWrite: + flags |= Interop.Sys.OpenFlags.O_RDWR; + break; + + case FileAccess.Write: + flags |= Interop.Sys.OpenFlags.O_WRONLY; + break; + } + + // Handle Inheritable, other FileShare flags are handled by Init + if ((share & FileShare.Inheritable) == 0) + { + flags |= Interop.Sys.OpenFlags.O_CLOEXEC; + } + + // Translate some FileOptions; some just aren't supported, and others will be handled after calling open. + // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true + // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose + // - Encrypted: No equivalent on Unix and is ignored + // - RandomAccess: Implemented after open if posix_fadvise is available + // - SequentialScan: Implemented after open if posix_fadvise is available + // - WriteThrough: Handled here + if ((options & FileOptions.WriteThrough) != 0) + { + flags |= Interop.Sys.OpenFlags.O_SYNC; + } + + return flags; + } + } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs similarity index 59% rename from src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs index 6e218d8c3e3b9..66c1f1b671ba1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs @@ -2,24 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace System.IO { - internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase + // this type defines a set of stateless FileStreamStrategy helper methods + internal static class FileStreamStrategyHelper { - protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { - return CreateFileOpenHandle(mode, share, options); + return CreateFileOpenHandle(path, mode, access, share, options); } - private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options) + private static unsafe SafeFileHandle CreateFileOpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); int fAccess = - ((_access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) | - ((_access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0); + ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) | + ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0); // Our Inheritable bit was stolen from Windows, but should be set in // the security attributes class. Don't leave this bit set. @@ -39,9 +41,11 @@ private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare shar using (DisableMediaInsertionPrompt.Create()) { - Debug.Assert(_path != null); + Debug.Assert(path != null); return ValidateFileHandle( - Interop.Kernel32.CreateFile(_path, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero)); + Interop.Kernel32.CreateFile(path, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero), + path, + (options & FileOptions.Asynchronous) != 0); } } @@ -57,7 +61,6 @@ internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsyn uint fileMode; - int status = Interop.NtDll.NtQueryInformationFile( FileHandle: fileHandle, IoStatusBlock: out _, @@ -89,7 +92,7 @@ internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsyn return (fileMode & (Interop.NtDll.FILE_SYNCHRONOUS_IO_ALERT | Interop.NtDll.FILE_SYNCHRONOUS_IO_NONALERT)) > 0; } - private static void VerifyHandleIsSync(SafeFileHandle handle) + internal static void VerifyHandleIsSync(SafeFileHandle handle) { // As we can accurately check the handle type when we have access to NtQueryInformationFile we don't need to skip for // any particular file handle type. @@ -102,5 +105,40 @@ private static void VerifyHandleIsSync(SafeFileHandle handle) if (!(IsHandleSynchronous(handle, ignoreInvalid: false) ?? true)) throw new ArgumentException(SR.Arg_HandleNotSync, nameof(handle)); } + + private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) + { + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; + if ((share & FileShare.Inheritable) != 0) + { + secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES + { + nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), + bInheritHandle = Interop.BOOL.TRUE + }; + } + return secAttrs; + } + + private static SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle, string path, bool useAsyncIO) + { + if (fileHandle.IsInvalid) + { + // Return a meaningful exception with the full path. + + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastWin32Error(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && path!.Length == PathInternal.GetRootLength(path)) + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + + fileHandle.IsAsync = useAsyncIO; + return fileHandle; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 41d7e2cf671b1..3ffdde2830426 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -41,10 +41,6 @@ internal UnixFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, Fi internal UnixFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream, path, mode, access, share, bufferSize, options) - { - } - - protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) @@ -54,30 +50,13 @@ protected override SafeFileHandle OpenHandle(FileMode mode, FileShare share, Fil if (_useAsyncIO) _asyncState = new AsyncState(); - - // Translate the arguments into arguments for an open call. - Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, _access, share, options); - - // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and - // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out - // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the - // actual permissions will typically be less than what we select here. - const Interop.Sys.Permissions OpenPermissions = - Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR | - Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | - Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; - - // Open the file and store the safe handle. - return SafeFileHandle.Open(_path!, openFlags, (int)OpenPermissions); } - internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsync) => handle.IsAsync ?? defaultIsAsync; - /// Initializes a stream for reading or writing a Unix file. /// How the file should be opened. /// What other access to the file should be allowed. This is currently ignored. /// The original path specified for the FileStream. - protected override void Init(FileMode mode, FileShare share, string originalPath) + protected override void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) { _fileHandle.IsAsync = _useAsyncIO; @@ -102,8 +81,8 @@ protected override void Init(FileMode mode, FileShare share, string originalPath // and Sequential together doesn't make sense as they are two competing options on the same spectrum, // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided). Interop.Sys.FileAdvice fadv = - (_options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM : - (_options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL : + (options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM : + (options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL : 0; if (fadv != 0) { @@ -111,7 +90,7 @@ protected override void Init(FileMode mode, FileShare share, string originalPath ignoreNotSupported: true); // just a hint. } - if (_mode == FileMode.Append) + if (mode == FileMode.Append) { // Jump to the end of the file if opened as Append. _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); @@ -144,71 +123,6 @@ protected override void InitFromHandle(SafeFileHandle handle, FileAccess access, SeekCore(handle, 0, SeekOrigin.Current); } - /// Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file. - /// The FileMode provided to the stream's constructor. - /// The FileAccess provided to the stream's constructor - /// The FileShare provided to the stream's constructor - /// The FileOptions provided to the stream's constructor - /// The flags value to be passed to the open system call. - private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options) - { - // Translate FileMode. Most of the values map cleanly to one or more options for open. - Interop.Sys.OpenFlags flags = default; - switch (mode) - { - default: - case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. - case FileMode.Truncate: // We truncate the file after getting the lock - break; - - case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later - case FileMode.OpenOrCreate: - case FileMode.Create: // We truncate the file after getting the lock - flags |= Interop.Sys.OpenFlags.O_CREAT; - break; - - case FileMode.CreateNew: - flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); - break; - } - - // Translate FileAccess. All possible values map cleanly to corresponding values for open. - switch (access) - { - case FileAccess.Read: - flags |= Interop.Sys.OpenFlags.O_RDONLY; - break; - - case FileAccess.ReadWrite: - flags |= Interop.Sys.OpenFlags.O_RDWR; - break; - - case FileAccess.Write: - flags |= Interop.Sys.OpenFlags.O_WRONLY; - break; - } - - // Handle Inheritable, other FileShare flags are handled by Init - if ((share & FileShare.Inheritable) == 0) - { - flags |= Interop.Sys.OpenFlags.O_CLOEXEC; - } - - // Translate some FileOptions; some just aren't supported, and others will be handled after calling open. - // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true - // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose - // - Encrypted: No equivalent on Unix and is ignored - // - RandomAccess: Implemented after open if posix_fadvise is available - // - SequentialScan: Implemented after open if posix_fadvise is available - // - WriteThrough: Handled here - if ((options & FileOptions.WriteThrough) != 0) - { - flags |= Interop.Sys.OpenFlags.O_SYNC; - } - - return flags; - } - /// Gets a value indicating whether the current stream supports seeking. public override bool CanSeek => CanSeekCore(_fileHandle); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index 11dceec0d0532..b879f1b6d9b5b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -61,7 +61,7 @@ internal WindowsFileStreamStrategy(FileStream fileStream, string path, FileMode { } - protected override void Init(FileMode mode, FileShare share, string originalPath) + protected override void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) { if (!PathInternal.IsExtended(originalPath)) { @@ -182,7 +182,7 @@ private void InitFromHandleImpl(SafeFileHandle handle, bool useAsyncIO) } else if (!useAsyncIO) { - VerifyHandleIsSync(handle); + FileStreamStrategyHelper.VerifyHandleIsSync(handle); } if (_canSeek) @@ -191,20 +191,6 @@ private void InitFromHandleImpl(SafeFileHandle handle, bool useAsyncIO) _filePosition = 0; } - private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) - { - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; - if ((share & FileShare.Inheritable) != 0) - { - secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES - { - nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), - bInheritHandle = Interop.BOOL.TRUE - }; - } - return secAttrs; - } - private bool HasActiveBufferOperation => !_activeBufferOperation.IsCompleted; public override bool CanSeek => _canSeek; @@ -1578,26 +1564,5 @@ internal override void Unlock(long position, long length) throw Win32Marshal.GetExceptionForLastWin32Error(_path); } } - - private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle) - { - if (fileHandle.IsInvalid) - { - // Return a meaningful exception with the full path. - - // NT5 oddity - when trying to open "C:\" as a Win32FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path!.Length == PathInternal.GetRootLength(_path)) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); - } - - fileHandle.IsAsync = _useAsyncIO; - return fileHandle; - } } } From 692b965181c4f4dfd7a14ae8a60d9af8c3400ab4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 25 Jan 2021 18:12:38 +0100 Subject: [PATCH 086/100] introduce ChooseStrategy method, remove #ifs --- .../src/System/IO/FileStream.cs | 18 +++--------------- .../System/IO/FileStreamStrategyHelper.Unix.cs | 7 +++++++ .../IO/FileStreamStrategyHelper.Windows.cs | 18 ++++++++++++++++-- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 8b31abc67cfb3..595d80df4ba19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -45,11 +45,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS { ValidateHandle(safeHandle, access, bufferSize, isAsync); -#if TARGET_WINDOWS - _strategy = WrapForDerivedType(new WindowsFileStreamStrategy(this, safeHandle, access, bufferSize, isAsync)); -#else - _strategy = WrapForDerivedType(new UnixFileStreamStrategy(this, safeHandle, access, bufferSize, isAsync)); -#endif + _strategy = WrapForDerivedType(FileStreamStrategyHelper.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync)); } catch { @@ -98,11 +94,7 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool { ValidateHandle(handle, access, bufferSize, isAsync); -#if TARGET_WINDOWS - _strategy = WrapForDerivedType(new WindowsFileStreamStrategy(this, handle, access, bufferSize, isAsync)); -#else - _strategy = WrapForDerivedType(new UnixFileStreamStrategy(this, handle, access, bufferSize, isAsync)); -#endif + _strategy = WrapForDerivedType(FileStreamStrategyHelper.ChooseStrategy(this, handle, access, bufferSize, isAsync)); } public FileStream(string path, FileMode mode) : @@ -171,11 +163,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } -#if TARGET_WINDOWS - _strategy = WrapForDerivedType(new WindowsFileStreamStrategy(this, path, mode, access, share, bufferSize, options)); -#else - _strategy = WrapForDerivedType(new UnixFileStreamStrategy(this, path, mode, access, share, bufferSize, options)); -#endif + _strategy = WrapForDerivedType(FileStreamStrategyHelper.ChooseStrategy(this, path, mode, access, share, bufferSize, options)); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs index 4d313383a7524..8f1e1b71300a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs @@ -13,6 +13,13 @@ namespace System.IO // this type defines a set of stateless FileStreamStrategy helper methods internal static class FileStreamStrategyHelper { + // in the future we are most probably going to introduce more strategies (io_uring etc) + internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + => new UnixFileStreamStrategy(fileStream, handle, access, bufferSize, isAsync); + + internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + => new UnixFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); + internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { // Translate the arguments into arguments for an open call. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs index 66c1f1b671ba1..5f7bad38c5627 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs @@ -10,11 +10,25 @@ namespace System.IO // this type defines a set of stateless FileStreamStrategy helper methods internal static class FileStreamStrategyHelper { - internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - return CreateFileOpenHandle(path, mode, access, share, options); + // the switch exitst to measure the overhead of introducing a factory method to the FileStream ctor + // we are going to have more implementations soon and then it's going to make more sense + switch (isAsync) + { + case true: + return new WindowsFileStreamStrategy(fileStream, handle, access, bufferSize, true); + case false: + return new WindowsFileStreamStrategy(fileStream, handle, access, bufferSize, false); + } } + internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + => new WindowsFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); + + internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + => CreateFileOpenHandle(path, mode, access, share, options); + private static unsafe SafeFileHandle CreateFileOpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); From fe202db88ff34309a5e8c578a142edb4e932654d Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 25 Jan 2021 18:37:24 +0100 Subject: [PATCH 087/100] fix the Unix build? --- .../src/System/IO/FileStreamStrategyHelper.Unix.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs index 8f1e1b71300a1..34629adc38333 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs @@ -105,3 +105,4 @@ private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mo return flags; } } +} From 1258ad7f783bd7bf956df1aa4a4cb08cb5803553 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 25 Jan 2021 18:49:58 +0100 Subject: [PATCH 088/100] fix the Unix build --- .../src/System/IO/UnixFileStreamStrategy.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs index 3ffdde2830426..840a57d835000 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs @@ -56,6 +56,7 @@ internal UnixFileStreamStrategy(FileStream fileStream, string path, FileMode mod /// How the file should be opened. /// What other access to the file should be allowed. This is currently ignored. /// The original path specified for the FileStream. + /// Options, passed via arguments as we have no guarantee that _options field was already set. protected override void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) { _fileHandle.IsAsync = _useAsyncIO; From d9fc921abf52b291a02e113980c377d3d443fd44 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 5 Feb 2021 11:36:28 +0100 Subject: [PATCH 089/100] rename FileStreamStrategyHelper => FileStreamHelpers --- .../src/System.Private.CoreLib.Shared.projitems | 4 ++-- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 8 ++++---- ...amStrategyHelper.Unix.cs => FileStreamHelpers.Unix.cs} | 2 +- ...tegyHelper.Windows.cs => FileStreamHelpers.Windows.cs} | 2 +- .../src/System/IO/FileStreamStrategyBase.cs | 2 +- .../src/System/IO/WindowsFileStreamStrategy.cs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamStrategyHelper.Unix.cs => FileStreamHelpers.Unix.cs} (99%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamStrategyHelper.Windows.cs => FileStreamHelpers.Windows.cs} (99%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 9c387b0ab38d7..5f591a6705eb8 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1615,7 +1615,7 @@ - + @@ -1822,7 +1822,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 595d80df4ba19..d0b66f317d8fd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -45,7 +45,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS { ValidateHandle(safeHandle, access, bufferSize, isAsync); - _strategy = WrapForDerivedType(FileStreamStrategyHelper.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync)); + _strategy = WrapForDerivedType(FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync)); } catch { @@ -86,7 +86,7 @@ public FileStream(SafeFileHandle handle, FileAccess access) } public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, FileStreamStrategyHelper.GetDefaultIsAsync(handle, DefaultIsAsync)) + : this(handle, access, bufferSize, FileStreamHelpers.GetDefaultIsAsync(handle, DefaultIsAsync)) { } @@ -94,7 +94,7 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool { ValidateHandle(handle, access, bufferSize, isAsync); - _strategy = WrapForDerivedType(FileStreamStrategyHelper.ChooseStrategy(this, handle, access, bufferSize, isAsync)); + _strategy = WrapForDerivedType(FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync)); } public FileStream(string path, FileMode mode) : @@ -163,7 +163,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } - _strategy = WrapForDerivedType(FileStreamStrategyHelper.ChooseStrategy(this, path, mode, access, share, bufferSize, options)); + _strategy = WrapForDerivedType(FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options)); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs index 34629adc38333..d2b67107a677c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO { // this type defines a set of stateless FileStreamStrategy helper methods - internal static class FileStreamStrategyHelper + internal static class FileStreamHelpers { // in the future we are most probably going to introduce more strategies (io_uring etc) internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs index 5f7bad38c5627..061b85dde852b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyHelper.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs @@ -8,7 +8,7 @@ namespace System.IO { // this type defines a set of stateless FileStreamStrategy helper methods - internal static class FileStreamStrategyHelper + internal static class FileStreamHelpers { internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs index 465d7dfded8ad..78858a231158e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs @@ -92,7 +92,7 @@ protected FileStreamStrategyBase(FileStream fileStream, string path, FileMode mo if ((options & FileOptions.Asynchronous) != 0) _useAsyncIO = true; - _fileHandle = FileStreamStrategyHelper.OpenHandle(fullPath, mode, access, share, options); + _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options); try { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs index b879f1b6d9b5b..53b504433921e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs @@ -182,7 +182,7 @@ private void InitFromHandleImpl(SafeFileHandle handle, bool useAsyncIO) } else if (!useAsyncIO) { - FileStreamStrategyHelper.VerifyHandleIsSync(handle); + FileStreamHelpers.VerifyHandleIsSync(handle); } if (_canSeek) From 2d820c31fa1b3a6c07c863730fa5ad7c378c3f15 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 5 Feb 2021 11:49:31 +0100 Subject: [PATCH 090/100] move all internal Base* methods to the bottom of the source file --- .../src/System/IO/FileStream.cs | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index d0b66f317d8fd..f8dde80586aeb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -213,9 +213,6 @@ public override Task FlushAsync(CancellationToken cancellationToken) return _strategy.FlushAsync(cancellationToken); } - internal Task BaseFlushAsync(CancellationToken cancellationToken) - => base.FlushAsync(cancellationToken); - public override int Read(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); @@ -225,8 +222,6 @@ public override int Read(byte[] buffer, int offset, int count) public override int Read(Span buffer) => _strategy.Read(buffer); - internal int BaseRead(Span buffer) => base.Read(buffer); - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ValidateBufferArguments(buffer, offset, count); @@ -240,9 +235,6 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel return _strategy.ReadAsync(buffer, offset, count, cancellationToken); } - internal Task BaseReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - => base.ReadAsync(buffer, offset, count, cancellationToken); - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) @@ -258,9 +250,6 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken return _strategy.ReadAsync(buffer, cancellationToken); } - internal ValueTask BaseReadAsync(Memory buffer, CancellationToken cancellationToken = default) - => base.ReadAsync(buffer, cancellationToken); - public override void Write(byte[] buffer, int offset, int count) { ValidateReadWriteArgs(buffer, offset, count); @@ -270,8 +259,6 @@ public override void Write(byte[] buffer, int offset, int count) public override void Write(ReadOnlySpan buffer) => _strategy.Write(buffer); - internal void BaseWrite(ReadOnlySpan buffer) => base.Write(buffer); - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ValidateBufferArguments(buffer, offset, count); @@ -285,9 +272,6 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati return _strategy.WriteAsync(buffer, offset, count, cancellationToken); } - internal Task BaseWriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - => base.WriteAsync(buffer, offset, count, cancellationToken); - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) @@ -303,9 +287,6 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return _strategy.WriteAsync(buffer, cancellationToken); } - internal ValueTask BaseWriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - => base.WriteAsync(buffer, cancellationToken); - /// /// Clears buffers for this stream and causes any buffered data to be written to the file. /// @@ -433,14 +414,9 @@ protected override void Dispose(bool disposing) public override ValueTask DisposeAsync() => _strategy.DisposeAsync(); - internal ValueTask BaseDisposeAsync() => base.DisposeAsync(); - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _strategy.CopyToAsync(destination, bufferSize, cancellationToken); - internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - => base.CopyToAsync(destination, bufferSize, cancellationToken); - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); @@ -450,9 +426,6 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy return _strategy.BeginRead(buffer, offset, count, callback, state); } - internal IAsyncResult BaseBeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => base.BeginRead(buffer, offset, count, callback, state); - public override int EndRead(IAsyncResult asyncResult) { if (asyncResult == null) @@ -461,8 +434,6 @@ public override int EndRead(IAsyncResult asyncResult) return _strategy.EndRead(asyncResult); } - internal int BaseEndRead(IAsyncResult asyncResult) => base.EndRead(asyncResult); - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); @@ -472,9 +443,6 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As return _strategy.BeginWrite(buffer, offset, count, callback, state); } - internal IAsyncResult BaseBeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => base.BeginWrite(buffer, offset, count, callback, state); - public override void EndWrite(IAsyncResult asyncResult) { if (asyncResult == null) @@ -483,10 +451,42 @@ public override void EndWrite(IAsyncResult asyncResult) _strategy.EndWrite(asyncResult); } - internal void BaseEndWrite(IAsyncResult asyncResult) => base.EndWrite(asyncResult); - public override bool CanSeek => _strategy.CanSeek; public override long Seek(long offset, SeekOrigin origin) => _strategy.Seek(offset, origin); + + internal Task BaseFlushAsync(CancellationToken cancellationToken) + => base.FlushAsync(cancellationToken); + + internal int BaseRead(Span buffer) => base.Read(buffer); + + internal Task BaseReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => base.ReadAsync(buffer, offset, count, cancellationToken); + + internal ValueTask BaseReadAsync(Memory buffer, CancellationToken cancellationToken = default) + => base.ReadAsync(buffer, cancellationToken); + + internal void BaseWrite(ReadOnlySpan buffer) => base.Write(buffer); + + internal Task BaseWriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => base.WriteAsync(buffer, offset, count, cancellationToken); + + internal ValueTask BaseWriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => base.WriteAsync(buffer, cancellationToken); + + internal ValueTask BaseDisposeAsync() => base.DisposeAsync(); + + internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + => base.CopyToAsync(destination, bufferSize, cancellationToken); + + internal IAsyncResult BaseBeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => base.BeginRead(buffer, offset, count, callback, state); + + internal int BaseEndRead(IAsyncResult asyncResult) => base.EndRead(asyncResult); + + internal IAsyncResult BaseBeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => base.BeginWrite(buffer, offset, count, callback, state); + + internal void BaseEndWrite(IAsyncResult asyncResult) => base.EndWrite(asyncResult); } } From 9706f3d06d3915b46158a8f5d29bbe321abd5b05 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 5 Feb 2021 11:56:51 +0100 Subject: [PATCH 091/100] alphabetic order of files in project file --- .../src/System.Private.CoreLib.Shared.projitems | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 5f591a6705eb8..d337d3646239e 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -383,6 +383,7 @@ + @@ -397,7 +398,6 @@ - @@ -1613,14 +1613,14 @@ + - - + @@ -1819,13 +1819,13 @@ - - - + + + From a76ed8c84bc5ebcf172bb416b9059bcf5d2fd1c3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 5 Feb 2021 12:24:50 +0100 Subject: [PATCH 092/100] it looks like the internal FileStream.IsClosed can be removed as we don't have any types in CoreLib that use it --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index f8dde80586aeb..c1df57e3f6111 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -381,8 +381,6 @@ public override long Position } } - internal virtual bool IsClosed => _strategy.IsClosed; - /// /// Reads a byte from the file stream. Returns the byte cast to an int /// or -1 if reading from the end of the stream. From 6204ae5b2631db2108ec694821bed462f654cb37 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 5 Feb 2021 18:24:29 +0100 Subject: [PATCH 093/100] call virtual Can methods where we used to and still can --- .../tests/FileStream/Position.cs | 10 ++++++++++ .../tests/FileStream/SetLength.cs | 20 +++++++++++++++++++ .../src/System/IO/FileStream.cs | 12 +++++------ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs index 364e3c95b76a0..8157f6cc91efd 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs @@ -28,5 +28,15 @@ public void SetPositionAppendModify() Assert.Equal(length + 1, fs.Position); } } + + [Fact] + public void GetPositionThrowsForUnseekableFileStream() + { + string fileName = GetTestFilePath(); + using (FileStream fs = new UnseekableFileStream(fileName, FileMode.Create)) + { + Assert.Throws(() => _ = fs.Position); + } + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs index 27a2d42a55a32..67944a457716c 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs @@ -25,5 +25,25 @@ public void SetLengthAppendModifyThrows() Assert.Equal(length, fs.Length); } } + + [Fact] + public void SetLengthThrowsForUnseekableFileStream() + { + string fileName = GetTestFilePath(); + using (FileStream fs = new UnseekableFileStream(fileName, FileMode.Create)) + { + Assert.Throws(() => fs.SetLength(1)); + } + } + + [Fact] + public void GetLengthThrowsForUnseekableFileStream() + { + string fileName = GetTestFilePath(); + using (FileStream fs = new UnseekableFileStream(fileName, FileMode.Create)) + { + Assert.Throws(() => _ = fs.Length); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index c1df57e3f6111..fc1327115ffb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -332,9 +332,9 @@ public override void SetLength(long value) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - if (!_strategy.CanSeek) + if (!CanSeek) throw Error.GetSeekNotSupported(); - if (!_strategy.CanWrite) + if (!CanWrite) throw Error.GetWriteNotSupported(); _strategy.SetLength(value); @@ -354,7 +354,7 @@ public override long Length get { if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - if (!_strategy.CanSeek) throw Error.GetSeekNotSupported(); + if (!CanSeek) throw Error.GetSeekNotSupported(); return _strategy.Length; } } @@ -367,7 +367,7 @@ public override long Position if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - if (!_strategy.CanSeek) + if (!CanSeek) throw Error.GetSeekNotSupported(); return _strategy.Position; @@ -419,7 +419,7 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy { ValidateBufferArguments(buffer, offset, count); if (_strategy.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!_strategy.CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); return _strategy.BeginRead(buffer, offset, count, callback, state); } @@ -436,7 +436,7 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As { ValidateBufferArguments(buffer, offset, count); if (_strategy.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!_strategy.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); return _strategy.BeginWrite(buffer, offset, count, callback, state); } From a52f5a88e8802c0a3115d8ff3a43dca28c487b80 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 22 Feb 2021 18:50:54 +0100 Subject: [PATCH 094/100] don't separate Unix and Windows strategies, introduce LegacyFileStreamStrategy --- .../System.Private.CoreLib.Shared.projitems | 10 +-- .../IO/FileStreamCompletionSource.Win32.cs | 14 ++-- .../src/System/IO/FileStreamHelpers.Unix.cs | 4 +- .../System/IO/FileStreamHelpers.Windows.cs | 6 +- ...s => LegacyFileStreamStrategy.Lock.OSX.cs} | 2 +- ... => LegacyFileStreamStrategy.Lock.Unix.cs} | 2 +- ...gy.cs => LegacyFileStreamStrategy.Unix.cs} | 49 +++++------- ...cs => LegacyFileStreamStrategy.Windows.cs} | 40 ++++------ ...egyBase.cs => LegacyFileStreamStrategy.cs} | 80 +++++++------------ 9 files changed, 82 insertions(+), 125 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/IO/{UnixFileStreamStrategy.Lock.OSX.cs => LegacyFileStreamStrategy.Lock.OSX.cs} (86%) rename src/libraries/System.Private.CoreLib/src/System/IO/{UnixFileStreamStrategy.Lock.Unix.cs => LegacyFileStreamStrategy.Lock.Unix.cs} (93%) rename src/libraries/System.Private.CoreLib/src/System/IO/{UnixFileStreamStrategy.cs => LegacyFileStreamStrategy.Unix.cs} (95%) rename src/libraries/System.Private.CoreLib/src/System/IO/{WindowsFileStreamStrategy.cs => LegacyFileStreamStrategy.Windows.cs} (97%) rename src/libraries/System.Private.CoreLib/src/System/IO/{FileStreamStrategyBase.cs => LegacyFileStreamStrategy.cs} (89%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 9271b335aa5ae..8bcf1103554d3 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -402,11 +402,11 @@ - + @@ -1630,7 +1630,7 @@ - + @@ -1833,9 +1833,9 @@ - - - + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs index fb30f40a30f1b..c7b56290ca7ee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs @@ -9,7 +9,7 @@ namespace System.IO { - internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase + internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. @@ -25,7 +25,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource private static Action? s_cancelCallback; - private readonly WindowsFileStreamStrategy _stream; + private readonly LegacyFileStreamStrategy _stream; private readonly int _numBufferedBytes; private CancellationTokenRegistration _cancellationRegistration; #if DEBUG @@ -35,7 +35,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource private long _result; // Using long since this needs to be used in Interlocked APIs // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - protected FileStreamCompletionSource(WindowsFileStreamStrategy stream, int numBufferedBytes, byte[]? bytes) + protected FileStreamCompletionSource(LegacyFileStreamStrategy stream, int numBufferedBytes, byte[]? bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) { _numBufferedBytes = numBufferedBytes; @@ -132,8 +132,8 @@ internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated // overlapped was already in use by another operation). object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); - Debug.Assert(state is WindowsFileStreamStrategy || state is FileStreamCompletionSource); - FileStreamCompletionSource completionSource = state is WindowsFileStreamStrategy fs ? + Debug.Assert(state is LegacyFileStreamStrategy || state is FileStreamCompletionSource); + FileStreamCompletionSource completionSource = state is LegacyFileStreamStrategy fs ? fs._currentOverlappedOwner! : // must be owned (FileStreamCompletionSource)state!; Debug.Assert(completionSource != null); @@ -220,7 +220,7 @@ private static void Cancel(object? state) } } - public static FileStreamCompletionSource Create(WindowsFileStreamStrategy stream, int numBufferedBytesRead, ReadOnlyMemory memory) + public static FileStreamCompletionSource Create(LegacyFileStreamStrategy stream, int numBufferedBytesRead, ReadOnlyMemory memory) { // If the memory passed in is the stream's internal buffer, we can use the base FileStreamCompletionSource, // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived @@ -241,7 +241,7 @@ private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSour { private MemoryHandle _handle; // mutable struct; do not make this readonly - internal MemoryFileStreamCompletionSource(WindowsFileStreamStrategy stream, int numBufferedBytes, ReadOnlyMemory memory) : + internal MemoryFileStreamCompletionSource(LegacyFileStreamStrategy stream, int numBufferedBytes, ReadOnlyMemory memory) : base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes { _handle = memory.Pin(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs index d2b67107a677c..9a04f9290d74f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs @@ -15,10 +15,10 @@ internal static class FileStreamHelpers { // in the future we are most probably going to introduce more strategies (io_uring etc) internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - => new UnixFileStreamStrategy(fileStream, handle, access, bufferSize, isAsync); + => new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, isAsync); internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - => new UnixFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); + => new LegacyFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs index 061b85dde852b..12ce74432c6df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs @@ -17,14 +17,14 @@ internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFil switch (isAsync) { case true: - return new WindowsFileStreamStrategy(fileStream, handle, access, bufferSize, true); + return new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, true); case false: - return new WindowsFileStreamStrategy(fileStream, handle, access, bufferSize, false); + return new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, false); } } internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - => new WindowsFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); + => new LegacyFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) => CreateFileOpenHandle(path, mode, access, share, options); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs similarity index 86% rename from src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs index 0e51b9c35ff91..599b51694d179 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase + internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { internal override void Lock(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs similarity index 93% rename from src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs index 57e3c4b72de6f..5233dcdb7087f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO { - internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase + internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs similarity index 95% rename from src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs index 840a57d835000..bfa469f9f1b87 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase + internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { /// File mode. private FileMode _mode; @@ -34,13 +34,12 @@ internal sealed partial class UnixFileStreamStrategy : FileStreamStrategyBase /// Lazily-initialized value for whether the file supports seeking. private bool? _canSeek; - internal UnixFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - : base(fileStream, handle, access, bufferSize, isAsync) - { - } - - internal UnixFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - : base(fileStream, path, mode, access, share, bufferSize, options) + /// Initializes a stream for reading or writing a Unix file. + /// How the file should be opened. + /// What other access to the file should be allowed. This is currently ignored. + /// The original path specified for the FileStream. + /// Options, passed via arguments as we have no guarantee that _options field was already set. + private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) @@ -50,15 +49,7 @@ internal UnixFileStreamStrategy(FileStream fileStream, string path, FileMode mod if (_useAsyncIO) _asyncState = new AsyncState(); - } - /// Initializes a stream for reading or writing a Unix file. - /// How the file should be opened. - /// What other access to the file should be allowed. This is currently ignored. - /// The original path specified for the FileStream. - /// Options, passed via arguments as we have no guarantee that _options field was already set. - protected override void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) - { _fileHandle.IsAsync = _useAsyncIO; // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive @@ -115,7 +106,7 @@ protected override void Init(FileMode mode, FileShare share, string originalPath } /// Initializes a stream from an already open file handle (file descriptor). - protected override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { if (useAsyncIO) _asyncState = new AsyncState(); @@ -233,7 +224,7 @@ public override ValueTask DisposeAsync() // override may already exist on a derived type. if (_useAsyncIO && _writePos > 0) { - return new ValueTask(Task.Factory.StartNew(static s => ((UnixFileStreamStrategy)s!).Dispose(), this, + return new ValueTask(Task.Factory.StartNew(static s => ((LegacyFileStreamStrategy)s!).Dispose(), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); } @@ -241,7 +232,7 @@ public override ValueTask DisposeAsync() } /// Flushes the OS buffer. This does not flush the internal read/write buffer. - protected override void FlushOSBuffer() + private void FlushOSBuffer() { if (Interop.Sys.FSync(_fileHandle) < 0) { @@ -260,7 +251,7 @@ protected override void FlushOSBuffer() } } - protected override void FlushWriteBufferForWriteByte() + private void FlushWriteBufferForWriteByte() { #pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 _asyncState?.Wait(); @@ -270,7 +261,7 @@ protected override void FlushWriteBufferForWriteByte() } /// Writes any data in the write buffer to the underlying stream and resets the buffer. - protected override void FlushWriteBuffer(bool calledFromFinalizer = false) + private void FlushWriteBuffer(bool calledFromFinalizer = false) { AssertBufferInvariants(); if (_writePos > 0) @@ -303,7 +294,7 @@ public override void SetLength(long value) } /// Reads a block of bytes from the stream and writes the data in a given buffer. - protected override int ReadSpan(Span destination) + private int ReadSpan(Span destination) { PrepareForReading(); @@ -397,7 +388,7 @@ private unsafe int ReadNative(Span buffer) /// The token to monitor for cancellation requests. /// If the operation completes synchronously, the number of bytes read. /// A task that represents the asynchronous read operation. - protected override Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) + private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { Debug.Assert(_useAsyncIO); Debug.Assert(_asyncState != null); @@ -458,7 +449,7 @@ private unsafe int ReadNative(Span buffer) // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (UnixFileStreamStrategy)s!; + var thisRef = (LegacyFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -471,7 +462,7 @@ private unsafe int ReadNative(Span buffer) } /// Reads from the file handle into the buffer, overwriting anything in it. - protected override int FillReadBufferForReadByte() + private int FillReadBufferForReadByte() { #pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 _asyncState?.Wait(); @@ -482,7 +473,7 @@ protected override int FillReadBufferForReadByte() /// Writes a block of bytes to the file stream. /// The buffer containing data to write to the stream. - protected override void WriteSpan(ReadOnlySpan source) + private void WriteSpan(ReadOnlySpan source) { PrepareForWriting(); @@ -559,7 +550,7 @@ private unsafe void WriteNative(ReadOnlySpan source) /// The buffer to write data from. /// The token to monitor for cancellation requests. /// A task that represents the asynchronous write operation. - protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(_useAsyncIO); Debug.Assert(_asyncState != null); @@ -619,7 +610,7 @@ protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, Can // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (UnixFileStreamStrategy)s!; + var thisRef = (LegacyFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -697,7 +688,7 @@ public override long Seek(long offset, SeekOrigin origin) /// /// not used in Unix implementation /// The new position in the stream. - protected override long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { Debug.Assert(!fileHandle.IsClosed && CanSeekCore(fileHandle)); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs similarity index 97% rename from src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs index 53b504433921e..4eaf3d69d0e2f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs @@ -39,7 +39,7 @@ namespace System.IO { - internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase + internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. @@ -51,17 +51,7 @@ internal sealed partial class WindowsFileStreamStrategy : FileStreamStrategyBase private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped - internal WindowsFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - : base(fileStream, handle, access, bufferSize, isAsync) - { - } - - internal WindowsFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - : base(fileStream, path, mode, access, share, bufferSize, options) - { - } - - protected override void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) + private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) { if (!PathInternal.IsExtended(originalPath)) { @@ -128,7 +118,7 @@ protected override void Init(FileMode mode, FileShare share, string originalPath } } - protected override void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { #if DEBUG bool hadBinding = handle.ThreadPoolBinding != null; @@ -285,7 +275,7 @@ public override async ValueTask DisposeAsync() } } - protected override void FlushOSBuffer() + private void FlushOSBuffer() { if (!Interop.Kernel32.FlushFileBuffers(_fileHandle)) { @@ -313,12 +303,12 @@ private Task FlushWriteAsync(CancellationToken cancellationToken) return flushTask; } - protected override void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); + private void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); // Writes are buffered. Anytime the buffer fills up // (_writePos + delta > _bufferSize) or the buffer switches to reading // and there is left over data (_writePos > 0), this function must be called. - protected override void FlushWriteBuffer(bool calledFromFinalizer = false) + private void FlushWriteBuffer(bool calledFromFinalizer = false) { if (_writePos == 0) return; Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!"); @@ -406,7 +396,7 @@ private unsafe void SetLengthCore(long value) private FileStreamCompletionSource? CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); - protected override int ReadSpan(Span destination) + private int ReadSpan(Span destination) { Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), @@ -479,7 +469,7 @@ private void AssertCanRead() } /// Reads from the file handle into the buffer, overwriting anything in it. - protected override int FillReadBufferForReadByte() => + private int FillReadBufferForReadByte() => _useAsyncIO ? ReadNativeAsync(new Memory(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() : ReadNative(_buffer); @@ -601,7 +591,7 @@ public override long Seek(long offset, SeekOrigin origin) // This doesn't do argument checking. Necessary for SetLength, which must // set the file pointer beyond the end of the file. This will update the // internal position - protected override long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek"); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); @@ -622,7 +612,7 @@ protected override long SeekCore(SafeFileHandle fileHandle, long offset, SeekOri return ret; } - protected override void OnBufferAllocated() + partial void OnBufferAllocated() { Debug.Assert(_buffer != null); Debug.Assert(_preallocatedOverlapped == null); @@ -631,7 +621,7 @@ protected override void OnBufferAllocated() _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer); } - protected override void WriteSpan(ReadOnlySpan source) + private void WriteSpan(ReadOnlySpan source) { Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); @@ -729,7 +719,7 @@ private unsafe void WriteCore(ReadOnlySpan source) return; } - protected override Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) + private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { Debug.Assert(_useAsyncIO); if (!CanRead) throw Error.GetReadNotSupported(); @@ -942,7 +932,7 @@ private unsafe Task ReadNativeAsync(Memory destination, int numBuffer return completionSource.Task; } - protected override ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(_useAsyncIO); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -1466,7 +1456,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion internal static readonly IOCompletionCallback s_callback = IOCallback; /// The FileStream that owns this instance. - internal readonly WindowsFileStreamStrategy _fileStream; + internal readonly LegacyFileStreamStrategy _fileStream; /// Tracked position representing the next location from which to read. internal long _position; @@ -1487,7 +1477,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion internal object CancellationLock => this; /// Initialize the awaitable. - internal AsyncCopyToAwaitable(WindowsFileStreamStrategy fileStream) + internal AsyncCopyToAwaitable(LegacyFileStreamStrategy fileStream) { _fileStream = fileStream; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs similarity index 89% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs index 78858a231158e..15833720ffde0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategyBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs @@ -9,30 +9,30 @@ namespace System.IO { - // This type exist so we can avoid code duplication between UnixFileStreamStrategy and WindowsFileStreamStrategy. + // This type exist so we can avoid code duplication between LegacyFileStreamStrategy and LegacyFileStreamStrategy. // As of now, it implements the Template pattern and defines multiple virtual methods, but in the future when we - // separate WindowsFileStreamStrategy into two separate strategies (Sync and Async), + // separate LegacyFileStreamStrategy into two separate strategies (Sync and Async), // it should be just defining fields present in all implementations (and the non-virtual methods that use them) - internal abstract class FileStreamStrategyBase : FileStreamStrategy + internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { - protected byte[]? _buffer; - protected readonly int _bufferLength; - protected readonly SafeFileHandle _fileHandle; // only ever null if ctor throws + private byte[]? _buffer; + private readonly int _bufferLength; + private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws /// Whether the file is opened for reading, writing, or both. - protected readonly FileAccess _access; + private readonly FileAccess _access; /// The path to the opened file. - protected readonly string? _path; + private readonly string? _path; /// The next available byte to be read from the _buffer. - protected int _readPos; + private int _readPos; /// The number of valid bytes in _buffer. - protected int _readLength; + private int _readLength; /// The next location in which a write should occur to the buffer. - protected int _writePos; + private int _writePos; /// /// Whether asynchronous read/write/flush operations should be performed using async I/O. @@ -49,22 +49,22 @@ internal abstract class FileStreamStrategyBase : FileStreamStrategy /// delegate to the base stream, and no attempt is made to synchronize. If async, we use /// a semaphore to coordinate both sync and async operations. /// - protected readonly bool _useAsyncIO; + private readonly bool _useAsyncIO; /// cached task for read ops that complete synchronously - protected Task? _lastSynchronouslyCompletedTask; + private Task? _lastSynchronouslyCompletedTask; /// /// Currently cached position in the stream. This should always mirror the underlying file's actual position, /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which /// point we attempt to error out. /// - protected long _filePosition; + private long _filePosition; /// Whether the file stream's handle has been exposed. - protected bool _exposedHandle; + private bool _exposedHandle; - protected FileStreamStrategyBase(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) + internal LegacyFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) { _exposedHandle = true; _bufferLength = bufferSize; @@ -81,7 +81,7 @@ protected FileStreamStrategyBase(FileStream fileStream, SafeFileHandle handle, F _fileHandle = handle; } - protected FileStreamStrategyBase(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) + internal LegacyFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) { string fullPath = Path.GetFullPath(path); @@ -108,39 +108,13 @@ protected FileStreamStrategyBase(FileStream fileStream, string path, FileMode mo } } - ~FileStreamStrategyBase() + ~LegacyFileStreamStrategy() { // it looks like having this finalizer is mandatory, // as we can not guarantee that the Strategy won't be null in FileStream finalizer Dispose(false); } - protected abstract void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO); - - protected abstract void Init(FileMode mode, FileShare share, string originalPath, FileOptions options); - - protected abstract void FlushWriteBuffer(bool calledFromFinalizer = false); - - protected abstract void FlushOSBuffer(); - - protected virtual void OnBufferAllocated() - { - } - - protected abstract int FillReadBufferForReadByte(); - - protected abstract void FlushWriteBufferForWriteByte(); - - protected abstract int ReadSpan(Span destination); - - protected abstract void WriteSpan(ReadOnlySpan source); - - protected abstract Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult); - - protected abstract ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken); - - protected abstract long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false); - internal override void DisposeInternal(bool disposing) => Dispose(disposing); public override Task FlushAsync(CancellationToken cancellationToken) @@ -337,7 +311,7 @@ internal override SafeFileHandle SafeFileHandle /// This will fail if someone else moved the UnixFileStream's handle or if /// our position updating code is incorrect. /// - protected void VerifyOSHandlePosition() + private void VerifyOSHandlePosition() { bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it #if DEBUG @@ -364,7 +338,7 @@ protected void VerifyOSHandlePosition() /// Verifies that state relating to the read/write buffer is consistent. [Conditional("DEBUG")] - protected void AssertBufferInvariants() + private void AssertBufferInvariants() { // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength); @@ -377,7 +351,7 @@ protected void AssertBufferInvariants() } /// Validates that we're ready to read from the stream. - protected void PrepareForReading() + private void PrepareForReading() { if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); @@ -412,7 +386,7 @@ public override long Position internal override bool IsClosed => _fileHandle.IsClosed; - protected static bool IsIoRelatedException(Exception e) => + private static bool IsIoRelatedException(Exception e) => // These all derive from IOException // DirectoryNotFoundException // DriveNotFoundException @@ -433,7 +407,7 @@ e is NotSupportedException || /// If the array hasn't been allocated, this will lazily allocate it. /// /// The buffer. - protected byte[] GetBuffer() + private byte[] GetBuffer() { Debug.Assert(_buffer == null || _buffer.Length == _bufferLength); if (_buffer == null) @@ -451,7 +425,7 @@ protected byte[] GetBuffer() /// reading from the stream, the data is dumped and our position in the underlying file /// is rewound as necessary. This does not flush the OS buffer. /// - protected void FlushInternalBuffer() + private void FlushInternalBuffer() { AssertBufferInvariants(); if (_writePos > 0) @@ -465,7 +439,7 @@ protected void FlushInternalBuffer() } /// Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary. - protected void FlushReadBuffer() + private void FlushReadBuffer() { // Reading is done by blocks from the file, but someone could read // 1 byte from the buffer then write. At that point, the OS's file @@ -528,7 +502,7 @@ public override void WriteByte(byte value) /// Validates that we're ready to write to the stream, /// including flushing a read buffer if necessary. /// - protected void PrepareForWriting() + private void PrepareForWriting() { if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); @@ -544,6 +518,8 @@ protected void PrepareForWriting() } } + partial void OnBufferAllocated(); + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { if (!_useAsyncIO) From 3a3b3c93f4521bef69b951f3932e4db0e5ba7663 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 22 Feb 2021 19:20:46 +0100 Subject: [PATCH 095/100] address code review comment and fix last TODO --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 91449fc854d9f..061be461300fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -168,7 +168,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle => _strategy.SafeFileHandle.DangerousGetHandle(); // TODO: is it OK that this method has some logic? + public virtual IntPtr Handle => _strategy.Handle; [UnsupportedOSPlatform("macos")] public virtual void Lock(long position, long length) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs index f4657da1495b0..97b5969355383 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs @@ -17,6 +17,8 @@ internal abstract class FileStreamStrategy : Stream internal abstract SafeFileHandle SafeFileHandle { get; } + internal IntPtr Handle => SafeFileHandle.DangerousGetHandle(); + internal abstract bool IsClosed { get; } internal abstract void Lock(long position, long length); From b41456af829b1ae88fc66f37c7cfe51f273f17a1 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 22 Feb 2021 19:27:46 +0100 Subject: [PATCH 096/100] fix Unix build 1/n --- .../src/System/IO/LegacyFileStreamStrategy.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs index bfa469f9f1b87..550b2acab2505 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs @@ -27,7 +27,7 @@ internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy /// the semaphore used to serialize all operation, the buffer/offset/count provided by the /// caller for ReadAsync/WriteAsync operations, and the last successful task returned /// synchronously from ReadAsync which can be reused if the count matches the next request. - /// Only initialized when is true. + /// Only initialized when is true. /// private AsyncState? _asyncState; From 41c3263f038c08279e521d3d0369d33e257a9107 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 22 Feb 2021 19:31:08 +0100 Subject: [PATCH 097/100] update comment --- .../src/System/IO/LegacyFileStreamStrategy.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs index 15833720ffde0..41327faf33d82 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs @@ -9,10 +9,7 @@ namespace System.IO { - // This type exist so we can avoid code duplication between LegacyFileStreamStrategy and LegacyFileStreamStrategy. - // As of now, it implements the Template pattern and defines multiple virtual methods, but in the future when we - // separate LegacyFileStreamStrategy into two separate strategies (Sync and Async), - // it should be just defining fields present in all implementations (and the non-virtual methods that use them) + // This type is partial so we can avoid code duplication between Windows and Unix Legacy implementations internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy { private byte[]? _buffer; From a88f1a5388ddf5c79f2c5d1aa30823fd52b9214b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 23 Feb 2021 19:51:24 +0100 Subject: [PATCH 098/100] Update src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs Co-authored-by: Stephen Toub --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 061be461300fa..538e03a29f5bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -407,10 +407,7 @@ public override long Position protected override void Dispose(bool disposing) { - if (_strategy != null) // possible in finalizer - { - _strategy.DisposeInternal(disposing); - } + _strategy?.DisposeInternal(disposing); // null _strategy possible in finalizer } public override ValueTask DisposeAsync() => _strategy.DisposeAsync(); From 5ad9cbbd9cd18da971b9164907fb37ee84e3b9b0 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 23 Feb 2021 20:02:52 +0100 Subject: [PATCH 099/100] apply code review suggestions --- .../src/System/IO/FileStream.cs | 8 ++++---- .../src/System/IO/FileStreamHelpers.Windows.cs | 12 +----------- .../src/System/IO/LegacyFileStreamStrategy.cs | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 538e03a29f5bc..078b0bd0a4186 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -46,7 +46,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS { ValidateHandle(safeHandle, access, bufferSize, isAsync); - _strategy = WrapForDerivedType(FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync)); + _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync)); } catch { @@ -78,7 +78,7 @@ private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); } - private FileStreamStrategy WrapForDerivedType(FileStreamStrategy impl) + private FileStreamStrategy WrapIfDerivedType(FileStreamStrategy impl) => GetType() == typeof(FileStream) ? impl : new DerivedFileStreamStrategy(this, impl); public FileStream(SafeFileHandle handle, FileAccess access) @@ -95,7 +95,7 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool { ValidateHandle(handle, access, bufferSize, isAsync); - _strategy = WrapForDerivedType(FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync)); + _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync)); } public FileStream(string path, FileMode mode) : @@ -164,7 +164,7 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } - _strategy = WrapForDerivedType(FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options)); + _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options)); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs index 12ce74432c6df..c1e27bc6a0706 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs @@ -11,17 +11,7 @@ namespace System.IO internal static class FileStreamHelpers { internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - { - // the switch exitst to measure the overhead of introducing a factory method to the FileStream ctor - // we are going to have more implementations soon and then it's going to make more sense - switch (isAsync) - { - case true: - return new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, true); - case false: - return new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, false); - } - } + => new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, isAsync); internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) => new LegacyFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs index 41327faf33d82..01e5db4f0827c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs @@ -68,7 +68,7 @@ internal LegacyFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, InitFromHandle(handle, access, isAsync); - // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // Note: It would be cleaner to set the following fields in ValidateHandle, // but we can't as they're readonly. _access = access; _useAsyncIO = isAsync; From 835fa17c56b011d7086541889304c83845c1aee4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 24 Feb 2021 08:27:31 +0100 Subject: [PATCH 100/100] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David CantĂș --- .../src/System/IO/FileStreamHelpers.Unix.cs | 2 +- .../src/System/IO/FileStreamHelpers.Windows.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs index 9a04f9290d74f..471005b833f74 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs @@ -10,7 +10,7 @@ namespace System.IO { - // this type defines a set of stateless FileStreamStrategy helper methods + // this type defines a set of stateless FileStream/FileStreamStrategy helper methods internal static class FileStreamHelpers { // in the future we are most probably going to introduce more strategies (io_uring etc) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs index c1e27bc6a0706..66dd3cb259e4f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs @@ -7,7 +7,7 @@ namespace System.IO { - // this type defines a set of stateless FileStreamStrategy helper methods + // this type defines a set of stateless FileStream/FileStreamStrategy helper methods internal static class FileStreamHelpers { internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)