Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce FileStreamStrategy as a first step of FileStream rewrite #47128

Merged
merged 101 commits into from
Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
c0ded00
rename FileStream -> FileStreamImpl FILES
adamsitnik Jan 15, 2021
0fe40d1
rename FileStream -> FileStreamImpl CLASS, make it internal and sealed
adamsitnik Jan 15, 2021
e352313
restore orgiginal FileStream
adamsitnik Jan 15, 2021
550e63f
ctors
adamsitnik Jan 15, 2021
62d863c
introduce new abstract class to have Lock, Unlock and Handle methods …
adamsitnik Jan 15, 2021
14d2aa0
implement FlushAsync
adamsitnik Jan 15, 2021
796d7a3
add all virtual methods to the base abstrac class (and rename it)
adamsitnik Jan 15, 2021
2108bd6
implement Read(byte[] buffer, int offset, int count)
adamsitnik Jan 15, 2021
3cc6d07
ReadAsync(byte[] buffer, int offset, int count, CancellationToken can…
adamsitnik Jan 15, 2021
0a202c0
ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = …
adamsitnik Jan 15, 2021
c45e7eb
Write(byte[] buffer, int offset, int count)
adamsitnik Jan 15, 2021
cedc13a
Write(ReadOnlySpan<byte> buffer)
adamsitnik Jan 15, 2021
4b3d048
WriteAsync(byte[] buffer, int offset, int count, CancellationToken ca…
adamsitnik Jan 15, 2021
bafc099
WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellatio…
adamsitnik Jan 15, 2021
91513ed
Flush() and Flush(bool flushToDisk)
adamsitnik Jan 15, 2021
ecd62f0
CanRead and CanWrite
adamsitnik Jan 15, 2021
1faf34a
input validation should be always performed in the public type
adamsitnik Jan 15, 2021
fe308fe
SetLength(long value)
adamsitnik Jan 15, 2021
082d630
SafeFileHandle, Name, IsAsync and Length
adamsitnik Jan 15, 2021
cb6adc3
Position
adamsitnik Jan 15, 2021
2feeefa
IsClosed
adamsitnik Jan 15, 2021
1301318
ReadByte()
adamsitnik Jan 15, 2021
7f15975
WriteByte(byte value)
adamsitnik Jan 15, 2021
6089931
finalizer
adamsitnik Jan 15, 2021
d26deb4
BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callba…
adamsitnik Jan 15, 2021
0a9fd33
BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callb…
adamsitnik Jan 15, 2021
f681b6f
EndRead and EndWrite
adamsitnik Jan 15, 2021
f880335
FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
adamsitnik Jan 15, 2021
fceeb0d
fix compilation errors
adamsitnik Jan 15, 2021
80fdf68
remove unused fields and methods
adamsitnik Jan 15, 2021
e1459f1
CanSeek and Seek
adamsitnik Jan 15, 2021
7cf008d
fix all compilation errors
adamsitnik Jan 15, 2021
d1d6528
add missing overrides (implemented in OS-specific files and not caugh…
adamsitnik Jan 15, 2021
d30f371
make sure FileStream is not used in FileStreamImpl
adamsitnik Jan 15, 2021
99868c4
rename FileStreamImplBase to FileStreamStrategy and move it to a sepa…
adamsitnik Jan 15, 2021
07ed850
it might seem to have to sense now, but we plan to introduce dedicate…
adamsitnik Jan 15, 2021
19ef1a7
some minor polishing
adamsitnik Jan 15, 2021
50a6850
reference field can be null in finalizer
adamsitnik Jan 19, 2021
a0b3196
it looks like having this finalizer is mandatory, as we can not guara…
adamsitnik Jan 19, 2021
e3b23fd
fix a typo in the comments
adamsitnik Jan 19, 2021
2581181
rename _actualImplementation to _impl as suggested in code review
adamsitnik Jan 19, 2021
62580e8
move ctor argument validation logic back to FileStream (it's going to…
adamsitnik Jan 19, 2021
82ac0d7
move handle validation logic to FileStream, make _bufferLength readonly
adamsitnik Jan 19, 2021
d35bd15
move more validation logic to FileStream
adamsitnik Jan 19, 2021
7a4c761
more polishing
adamsitnik Jan 19, 2021
7c95a6c
reduce code duplication in ctors
adamsitnik Jan 19, 2021
85ab921
the Unix implementation was assuming type check is not needed
adamsitnik Jan 19, 2021
ccfea9a
fix the Flush bug that I've introduced
adamsitnik Jan 20, 2021
97a8c51
make sure base.CopyToAsync is called for all custom types that Derive…
adamsitnik Jan 20, 2021
a288c2a
introduce DerivedFileStreamImpl, remove GetType checks from FileStream
adamsitnik Jan 20, 2021
662fa2c
all FileStream methods should just delegate the actual logic to Strategy
adamsitnik Jan 21, 2021
233df39
clarify all "base" keyword usages
adamsitnik Jan 21, 2021
b4b31c4
clarify all "base" keyword usages, make sure the comments are in the …
adamsitnik Jan 21, 2021
19cec28
Flush() called from strategy.SafeFileHandle should call the virtual F…
adamsitnik Jan 21, 2021
3a2f106
remove the if statement (it's going to become switch)
adamsitnik Jan 21, 2021
4e3796c
get rid of the Impl word, use Strategy everywhere
adamsitnik Jan 22, 2021
227d98d
remove .Handle from the Strategy
adamsitnik Jan 22, 2021
8c865a8
rename FileStreamImpl => CommonFileStreamStrategyTemplate
adamsitnik Jan 22, 2021
e82a235
rename FileStreamImpl.Unix => UnixFileStreamStrategy
adamsitnik Jan 22, 2021
8756821
rename FileStreamImpl.Windows => WindowsFileStreamStrategy
adamsitnik Jan 22, 2021
d7dd225
make the fields protected so they can be accessed from derived types
adamsitnik Jan 22, 2021
23446ee
remove handle
adamsitnik Jan 22, 2021
4793717
fix finalizer
adamsitnik Jan 22, 2021
6c79ac5
fix the ctors, make init methods abstract and part of the template
adamsitnik Jan 22, 2021
cc65678
Lock & Unlock
adamsitnik Jan 22, 2021
6d2db24
FlushWriteBuffer
adamsitnik Jan 22, 2021
01899df
FlushOSBuffer
adamsitnik Jan 22, 2021
c2cc411
there is no need to have selead methods in a sealed type...
adamsitnik Jan 22, 2021
a92424d
SetLength
adamsitnik Jan 22, 2021
e360787
Length {get;}
adamsitnik Jan 22, 2021
3791ecc
OnBufferAllocated
adamsitnik Jan 22, 2021
c1a418a
FillReadBufferForReadByte
adamsitnik Jan 22, 2021
dab77a3
FlushWriteBufferForWriteByte
adamsitnik Jan 22, 2021
6e8d201
ReadSpan WriteSpan
adamsitnik Jan 22, 2021
08bbb0f
ReadAsyncInternal WriteAsyncInternal
adamsitnik Jan 22, 2021
4e86030
SeekCore
adamsitnik Jan 22, 2021
c85c303
make the private helpers protected so they can be accessed from deriv…
adamsitnik Jan 22, 2021
5802b83
replaces usages of FileStreamImpl with appropriate Strategy
adamsitnik Jan 22, 2021
33f58df
remove Handle from DerivedFileStreamStrategy
adamsitnik Jan 22, 2021
2e20ab9
fix the ctors, it compiles on Windows!
adamsitnik Jan 22, 2021
1704b83
this is not needed anymore
adamsitnik Jan 22, 2021
34b7822
fix compilation errors for Unix
adamsitnik Jan 22, 2021
81cd5eb
fix the build?
adamsitnik Jan 22, 2021
0a4c55c
rename CommonFileStreamStrategyTemplate to FileStreamStrategyBase bef…
adamsitnik Jan 25, 2021
01f0cc3
introduce FileStreamStrategyHelper and move some of the parameterless…
adamsitnik Jan 25, 2021
692b965
introduce ChooseStrategy method, remove #ifs
adamsitnik Jan 25, 2021
fe202db
fix the Unix build?
adamsitnik Jan 25, 2021
1258ad7
fix the Unix build
adamsitnik Jan 25, 2021
d9fc921
rename FileStreamStrategyHelper => FileStreamHelpers
adamsitnik Feb 5, 2021
2d820c3
move all internal Base* methods to the bottom of the source file
adamsitnik Feb 5, 2021
9706f3d
alphabetic order of files in project file
adamsitnik Feb 5, 2021
a76ed8c
it looks like the internal FileStream.IsClosed can be removed as we d…
adamsitnik Feb 5, 2021
6204ae5
call virtual Can methods where we used to and still can
adamsitnik Feb 5, 2021
ba34492
Merge remote-tracking branch 'upstream/master' into fileStream1stStep
adamsitnik Feb 8, 2021
a52f5a8
don't separate Unix and Windows strategies, introduce LegacyFileStrea…
adamsitnik Feb 22, 2021
3a3b3c9
address code review comment and fix last TODO
adamsitnik Feb 22, 2021
b41456a
fix Unix build 1/n
adamsitnik Feb 22, 2021
41c3263
update comment
adamsitnik Feb 22, 2021
a88f1a5
Update src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs
adamsitnik Feb 23, 2021
5ad9cbb
apply code review suggestions
adamsitnik Feb 23, 2021
835fa17
Apply suggestions from code review
adamsitnik Feb 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileShare.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamStrategy.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\HandleInheritability.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\InvalidDataException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\IOException.cs" />
Expand Down Expand Up @@ -1611,8 +1613,8 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamImpl.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamImpl.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamCompletionSource.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" />
Expand Down Expand Up @@ -1816,9 +1818,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Lock.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Lock.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamImpl.Lock.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamImpl.Lock.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamImpl.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Names.Unix.cs" />
Expand Down
660 changes: 97 additions & 563 deletions src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace System.IO
{
public partial class FileStream : Stream
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.
Expand All @@ -25,7 +25,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource<int>

private static Action<object?>? s_cancelCallback;

private readonly FileStream _stream;
private readonly FileStreamImpl _stream;
private readonly int _numBufferedBytes;
private CancellationTokenRegistration _cancellationRegistration;
#if DEBUG
Expand All @@ -35,7 +35,7 @@ private unsafe class FileStreamCompletionSource : TaskCompletionSource<int>
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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -220,7 +220,7 @@ private static void Cancel(object? state)
}
}

public static FileStreamCompletionSource Create(FileStream stream, int numBufferedBytesRead, ReadOnlyMemory<byte> memory)
public static FileStreamCompletionSource Create(FileStreamImpl stream, int numBufferedBytesRead, ReadOnlyMemory<byte> 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
Expand All @@ -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<byte> memory) :
internal MemoryFileStreamCompletionSource(FileStreamImpl stream, int numBufferedBytes, ReadOnlyMemory<byte> memory) :
base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes
{
_handle = memory.Pin();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace System.IO
{
public partial class FileStream : Stream
internal sealed partial class FileStreamImpl : FileStreamStrategy
{
private static void LockInternal(long position, long length)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace System.IO
{
public partial class FileStream : Stream
internal sealed partial class FileStreamImpl : FileStreamStrategy
{
/// <summary>Prevents other processes from reading from or writing to the FileStream.</summary>
/// <param name="position">The beginning of the range to lock.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace System.IO
{
/// <summary>Provides an implementation of a file stream for Unix files.</summary>
public partial class FileStream : Stream
internal sealed partial class FileStreamImpl : FileStreamStrategy
{
/// <summary>File mode.</summary>
private FileMode _mode;
Expand Down Expand Up @@ -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;

/// <summary>Initializes a stream for reading or writing a Unix file.</summary>
/// <param name="mode">How the file should be opened.</param>
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -530,7 +530,7 @@ private unsafe int ReadNative(Span<byte> 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
{
Expand Down Expand Up @@ -691,7 +691,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> 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
{
Expand Down Expand Up @@ -775,7 +775,7 @@ public override long Seek(long offset, SeekOrigin origin)
/// <returns>The new position in the stream.</returns>
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System.IO
{
public partial class FileStream : Stream
internal sealed partial class FileStreamImpl : FileStreamStrategy
{
private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
{
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

namespace System.IO
{
public partial class FileStream : Stream
internal sealed partial class FileStreamImpl : FileStreamStrategy
{
private bool _canSeek;
private bool _isPipe; // Whether to disable async buffering code.
Expand Down Expand Up @@ -261,20 +261,15 @@ 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.
try
{
if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0)
{
await FlushAsyncInternal(default).ConfigureAwait(false);
await FlushAsync(default).ConfigureAwait(false);
}
}
finally
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -1474,7 +1469,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion
internal static readonly IOCompletionCallback s_callback = IOCallback;

/// <summary>The FileStream that owns this instance.</summary>
internal readonly FileStream _fileStream;
internal readonly FileStreamImpl _fileStream;

/// <summary>Tracked position representing the next location from which to read.</summary>
internal long _position;
Expand All @@ -1495,7 +1490,7 @@ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion
internal object CancellationLock => this;

/// <summary>Initialize the awaitable.</summary>
internal AsyncCopyToAwaitable(FileStream fileStream)
internal AsyncCopyToAwaitable(FileStreamImpl fileStream)
{
_fileStream = fileStream;
}
Expand Down
Loading