From e1c8dfd695516003235c967cb5c789df61beb169 Mon Sep 17 00:00:00 2001 From: dersia Date: Wed, 26 Jul 2023 00:21:52 +0200 Subject: [PATCH 1/4] added IAsyncDisposable to TextReader. Implemnted IAsyncDisposable for StreamReader as it is implemented for StreamWriter. Changed StreamWriter to call base.Dispose(disposing) and set _disposed to true, even if stream is not closable --- .../src/System/IO/StreamReader.cs | 60 +++++++++++++++---- .../src/System/IO/StreamWriter.cs | 24 ++++---- .../src/System/IO/TextReader.cs | 15 ++++- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index a7e5befbb4c25d..13fa6e96a3b75c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -245,27 +245,67 @@ protected override void Dispose(bool disposing) { return; } - _disposed = true; + try + { + if (!_disposed && disposing) + { + CheckAsyncTaskInProgress(); + } + } + finally + { + CloseStreamFromDispose(disposing); + } + } - // Dispose of our resources if this StreamReader is closable. - if (_closable) + private void CloseStreamFromDispose(bool disposing) + { + try { - try + // Dispose of our resources if this StreamReader is closable. + if (_closable && !_disposed) { - // Note that Stream.Close() can potentially throw here. So we need to - // ensure cleaning up internal resources, inside the finally block. + // Attempt to close the stream even + // Note that Stream.Close() can potentially throw here. + // In this case, we still need to ensure + // cleaning up internal resources, hence the finally block. if (disposing) { _stream.Close(); } } - finally + } + finally + { + _disposed = true; + _charPos = 0; + _charLen = 0; + base.Dispose(disposing); + } + } + + public override ValueTask DisposeAsync() => + GetType() != typeof(StreamReader) ? + base.DisposeAsync() : + DisposeAsyncCore(); + + private ValueTask DisposeAsyncCore() + { + // Same logic as in Dispose(). + Debug.Assert(GetType() == typeof(StreamReader)); + try + { + if (!_disposed) { - _charPos = 0; - _charLen = 0; - base.Dispose(disposing); + CheckAsyncTaskInProgress(); } } + finally + { + CloseStreamFromDispose(disposing: true); + } + GC.SuppressFinalize(this); + return ValueTask.CompletedTask; } public virtual Encoding CurrentEncoding => _encoding; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index df0c9e95cc6c05..779b4f61d5207e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -189,13 +189,17 @@ public override void Close() protected override void Dispose(bool disposing) { + if (_disposed) + { + return; + } try { // We need to flush any buffered data if we are being closed/disposed. // Also, we never close the handles for stdout & friends. So we can safely // write any buffered data to those streams even during finalization, which // is generally the right thing to do. - if (!_disposed && disposing) + if (disposing) { // Note: flush on the underlying stream can throw (ex., low disk space) CheckAsyncTaskInProgress(); @@ -210,10 +214,10 @@ protected override void Dispose(bool disposing) private void CloseStreamFromDispose(bool disposing) { - // Dispose of our resources if this StreamWriter is closable. - if (_closable && !_disposed) + try { - try + // Dispose of our resources if this StreamWriter is closable. + if (_closable && !_disposed) { // Attempt to close the stream even if there was an IO error from Flushing. // Note that Stream.Close() can potentially throw here (may or may not be @@ -224,12 +228,12 @@ private void CloseStreamFromDispose(bool disposing) _stream.Close(); } } - finally - { - _disposed = true; - _charLen = 0; - base.Dispose(disposing); - } + } + finally + { + _disposed = true; + _charLen = 0; + base.Dispose(disposing); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextReader.cs index 179a07e0a956e4..63e411a8e45bb9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/TextReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextReader.cs @@ -17,7 +17,7 @@ namespace System.IO // // This class is intended for character input, not bytes. // There are methods on the Stream class for reading bytes. - public abstract partial class TextReader : MarshalByRefObject, IDisposable + public abstract partial class TextReader : MarshalByRefObject, IDisposable, IAsyncDisposable { // Create our own instance to avoid static field initialization order problems on Mono. public static readonly TextReader Null = new StreamReader.NullStreamReader(); @@ -40,6 +40,19 @@ protected virtual void Dispose(bool disposing) { } + public virtual ValueTask DisposeAsync() + { + try + { + Dispose(); + return default; + } + catch (Exception exc) + { + return ValueTask.FromException(exc); + } + } + // Returns the next available character without actually reading it from // the input stream. The current position of the TextReader is not changed by // this operation. The returned value is -1 if no further characters are From 4c3b8cf4b908a5d31604e7743440916a75e67a53 Mon Sep 17 00:00:00 2001 From: dersia Date: Wed, 26 Jul 2023 01:37:49 +0200 Subject: [PATCH 2/4] fixed comment on StreamReader.CloseStreamFromDispose --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 13fa6e96a3b75c..61d430fc92e87c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -265,7 +265,7 @@ private void CloseStreamFromDispose(bool disposing) // Dispose of our resources if this StreamReader is closable. if (_closable && !_disposed) { - // Attempt to close the stream even + // Attempt to close the stream // Note that Stream.Close() can potentially throw here. // In this case, we still need to ensure // cleaning up internal resources, hence the finally block. From 888f9576e86bae54c46815e26ce86c9e7bff2925 Mon Sep 17 00:00:00 2001 From: dersia Date: Thu, 27 Jul 2023 01:41:39 +0200 Subject: [PATCH 3/4] Reverted Change to StreamWriter.CloseStreamFromDispose Added StreamWriter.CloseStreamFromDisposeAsync Added same for StreamReader Added missing Refs Added Tests --- .../tests/Stream/Stream.NullTests.cs | 117 ++++++++++++++++++ .../StreamReader/StreamReader.DisposeAsync.cs | 77 ++++++++++++ .../System.IO/tests/System.IO.Tests.csproj | 1 + .../tests/TextReader/TextReaderTests.cs | 26 ++++ .../src/System/IO/StreamReader.cs | 60 ++++++--- .../src/System/IO/StreamWriter.cs | 45 +++++-- .../System.Runtime/ref/System.Runtime.cs | 4 +- 7 files changed, 301 insertions(+), 29 deletions(-) create mode 100644 src/libraries/System.IO/tests/StreamReader/StreamReader.DisposeAsync.cs diff --git a/src/libraries/System.IO/tests/Stream/Stream.NullTests.cs b/src/libraries/System.IO/tests/Stream/Stream.NullTests.cs index efe700c1e9c753..e6a5c50b830bcc 100644 --- a/src/libraries/System.IO/tests/Stream/Stream.NullTests.cs +++ b/src/libraries/System.IO/tests/Stream/Stream.NullTests.cs @@ -33,6 +33,13 @@ public static void TestNullStream_Dispose() Stream.Null.Dispose(); // Dispose shouldn't have any side effects } + [Fact] + public static async Task TestNullStream_DisposeAsync() + { + await Stream.Null.DisposeAsync(); + await Stream.Null.DisposeAsync(); // Dispose shouldn't have any side effects + } + [Fact] public static async Task TestNullStream_CopyTo() { @@ -133,6 +140,16 @@ public static void TestNullTextReaderDispose(TextReader input) Assert.Equal("", input.ReadToEnd()); } + [Theory] + [MemberData(nameof(NullReaders))] + public static async Task TestNullTextReaderDisposeAsync(TextReader input) + { + // dispose should be a no-op + await input.DisposeAsync(); + await input.DisposeAsync(); + Assert.Equal("", input.ReadToEnd()); + } + [Theory] [MemberData(nameof(NullReaders))] public static void TestNullTextReader(TextReader input) @@ -172,6 +189,22 @@ public static async Task TestNullTextReaderAsync(TextReader input) input.Dispose(); } + [Theory] + [MemberData(nameof(NullReaders))] + public static async Task TestNullTextReaderAsync_DisposeAsync(TextReader input) + { + var chars = new char[2]; + Assert.Equal(0, await input.ReadAsync(chars, 0, chars.Length)); + Assert.Equal(0, await input.ReadAsync(chars.AsMemory(), default)); + Assert.Equal(0, await input.ReadBlockAsync(chars, 0, chars.Length)); + Assert.Equal(0, await input.ReadBlockAsync(chars.AsMemory(), default)); + Assert.Null(await input.ReadLineAsync()); + Assert.Null(await input.ReadLineAsync(default)); + Assert.Equal("", await input.ReadToEndAsync()); + Assert.Equal("", await input.ReadToEndAsync(default)); + await input.DisposeAsync(); + } + [Theory] [MemberData(nameof(NullReaders))] public static async Task TestCanceledNullTextReaderAsync(TextReader input) @@ -192,6 +225,76 @@ public static async Task TestCanceledNullTextReaderAsync(TextReader input) input.Dispose(); } + [Theory] + [MemberData(nameof(NullReaders))] + public static async Task TestCanceledNullTextReaderAsync_DisposeAsync(TextReader input) + { + using CancellationTokenSource tokenSource = new CancellationTokenSource(); + tokenSource.Cancel(); + var token = tokenSource.Token; + var chars = new char[2]; + OperationCanceledException ex; + ex = await Assert.ThrowsAnyAsync(async () => await input.ReadAsync(chars.AsMemory(), token)); + Assert.Equal(token, ex.CancellationToken); + ex = await Assert.ThrowsAnyAsync(async () => await input.ReadBlockAsync(chars.AsMemory(), token)); + Assert.Equal(token, ex.CancellationToken); + ex = await Assert.ThrowsAnyAsync(async () => await input.ReadLineAsync(token)); + Assert.Equal(token, ex.CancellationToken); + ex = await Assert.ThrowsAnyAsync(async () => await input.ReadToEndAsync(token)); + Assert.Equal(token, ex.CancellationToken); + await input.DisposeAsync(); + } + + [Theory] + [MemberData(nameof(NullReaders))] + public static void TestNullTextReader_Disposed(TextReader input) + { + StreamReader sr = input as StreamReader; + + input.Dispose(); + + if (sr != null) + Assert.True(sr.EndOfStream, "EndOfStream property didn't return true"); + Assert.Null(input.ReadLine()); + if (sr != null) + Assert.True(sr.EndOfStream, "EndOfStream property didn't return true"); + + Assert.Equal(-1, input.Read()); + Assert.Equal(-1, input.Peek()); + var chars = new char[2]; + Assert.Equal(0, input.Read(chars, 0, chars.Length)); + Assert.Equal(0, input.Read(chars.AsSpan())); + Assert.Equal(0, input.ReadBlock(chars, 0, chars.Length)); + Assert.Equal(0, input.ReadBlock(chars.AsSpan())); + Assert.Equal("", input.ReadToEnd()); + input.Dispose(); + } + + [Theory] + [MemberData(nameof(NullReaders))] + public static async Task TestNullTextReader_DisposedAsync(TextReader input) + { + StreamReader sr = input as StreamReader; + + await input.DisposeAsync(); + + if (sr != null) + Assert.True(sr.EndOfStream, "EndOfStream property didn't return true"); + Assert.Null(input.ReadLine()); + if (sr != null) + Assert.True(sr.EndOfStream, "EndOfStream property didn't return true"); + + Assert.Equal(-1, input.Read()); + Assert.Equal(-1, input.Peek()); + var chars = new char[2]; + Assert.Equal(0, await input.ReadAsync(chars, 0, chars.Length)); + Assert.Equal(0, input.Read(chars.AsSpan())); + Assert.Equal(0, await input.ReadBlockAsync(chars, 0, chars.Length)); + Assert.Equal(0, input.ReadBlock(chars.AsSpan())); + Assert.Equal("", await input.ReadToEndAsync()); + await input.DisposeAsync(); + } + [Theory] [MemberData(nameof(NullWriters))] public static void TextNullTextWriter(TextWriter output) @@ -206,6 +309,20 @@ public static void TextNullTextWriter(TextWriter output) output.Dispose(); } + [Theory] + [MemberData(nameof(NullWriters))] + public static async Task TextNullTextWriterAsync(TextWriter output) + { + await output.FlushAsync(); + await output.DisposeAsync(); + + await output.WriteLineAsync(decimal.MinValue.ToString()); + await output.WriteLineAsync(Math.PI.ToString()); + await output.WriteLineAsync(output.NewLine); + await output.FlushAsync(); + await output.DisposeAsync(); + } + [Theory] [MemberData(nameof(NullStream_ReadWriteData))] public void TestNullStream_ReadSpan(byte[] buffer, int offset, int count) diff --git a/src/libraries/System.IO/tests/StreamReader/StreamReader.DisposeAsync.cs b/src/libraries/System.IO/tests/StreamReader/StreamReader.DisposeAsync.cs new file mode 100644 index 00000000000000..2a4a6490ddb42d --- /dev/null +++ b/src/libraries/System.IO/tests/StreamReader/StreamReader.DisposeAsync.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public class StreamReader_disposeAsyncTest + { + + [Fact] + public void DisposeAsync_CanInvokeMultipleTimes() + { + var ms = new MemoryStream(); + var sr = new StreamReader(ms); + Assert.True(sr.DisposeAsync().IsCompletedSuccessfully); + Assert.True(sr.DisposeAsync().IsCompletedSuccessfully); + } + + [Fact] + public void DisposeAsync_CanDisposeAsyncAfterDispose() + { + var ms = new MemoryStream(); + var sr = new StreamReader(ms); + sr.Dispose(); + Assert.True(sr.DisposeAsync().IsCompletedSuccessfully); + } + + [Fact] + public async Task DisposeAsync_LeaveOpenTrue_LeftOpen() + { + var ms = new MemoryStream(); + var sr = new StreamReader(ms, Encoding.ASCII, false, 0x1000, leaveOpen: true); + await sr.DisposeAsync(); + Assert.Equal(0, ms.Position); // doesn't throw + } + + [Fact] + public async Task DisposeAsync_DerivedTypeForcesDisposeToBeUsedUnlessOverridden() + { + var ms = new MemoryStream(); + var sr = new OverrideDisposeStreamReader(ms); + Assert.False(sr.DisposeInvoked); + await sr.DisposeAsync(); + Assert.True(sr.DisposeInvoked); + } + + [Fact] + public async Task DisposeAsync_DerivedTypeDisposeAsyncInvoked() + { + var ms = new MemoryStream(); + var sr = new OverrideDisposeAndDisposeAsyncStreamReader(ms); + Assert.False(sr.DisposeInvoked); + Assert.False(sr.DisposeAsyncInvoked); + await sr.DisposeAsync(); + Assert.False(sr.DisposeInvoked); + Assert.True(sr.DisposeAsyncInvoked); + } + + private sealed class OverrideDisposeStreamReader : StreamReader + { + public bool DisposeInvoked; + public OverrideDisposeStreamReader(Stream output) : base(output) { } + protected override void Dispose(bool disposing) => DisposeInvoked = true; + } + + private sealed class OverrideDisposeAndDisposeAsyncStreamReader : StreamReader + { + public bool DisposeInvoked, DisposeAsyncInvoked; + public OverrideDisposeAndDisposeAsyncStreamReader(Stream output) : base(output) { } + protected override void Dispose(bool disposing) => DisposeInvoked = true; + public override ValueTask DisposeAsync() { DisposeAsyncInvoked = true; return default; } + } + } +} diff --git a/src/libraries/System.IO/tests/System.IO.Tests.csproj b/src/libraries/System.IO/tests/System.IO.Tests.csproj index c7286f40f1518e..8c098e8f806989 100644 --- a/src/libraries/System.IO/tests/System.IO.Tests.csproj +++ b/src/libraries/System.IO/tests/System.IO.Tests.csproj @@ -29,6 +29,7 @@ + diff --git a/src/libraries/System.IO/tests/TextReader/TextReaderTests.cs b/src/libraries/System.IO/tests/TextReader/TextReaderTests.cs index d18e477153d6d7..21a4ff7f135e75 100644 --- a/src/libraries/System.IO/tests/TextReader/TextReaderTests.cs +++ b/src/libraries/System.IO/tests/TextReader/TextReaderTests.cs @@ -310,5 +310,31 @@ public void ReadBlockSpan() } } } + + [Fact] + public void DisposeAsync_InvokesDisposeSynchronously() + { + bool disposeInvoked = false; + var tw = new InvokeActionOnDisposeTextReader() { DisposeAction = () => disposeInvoked = true }; + Assert.False(disposeInvoked); + Assert.True(tw.DisposeAsync().IsCompletedSuccessfully); + Assert.True(disposeInvoked); + } + + [Fact] + public void DisposeAsync_ExceptionReturnedInTask() + { + Exception e = new FormatException(); + var tw = new InvokeActionOnDisposeTextReader() { DisposeAction = () => { throw e; } }; + ValueTask vt = tw.DisposeAsync(); + Assert.True(vt.IsFaulted); + Assert.Same(e, vt.AsTask().Exception.InnerException); + } + + private sealed class InvokeActionOnDisposeTextReader : TextReader + { + public Action DisposeAction; + protected override void Dispose(bool disposing) => DisposeAction?.Invoke(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 61d430fc92e87c..17cd948d04e892 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -247,7 +247,7 @@ protected override void Dispose(bool disposing) } try { - if (!_disposed && disposing) + if (disposing) { CheckAsyncTaskInProgress(); } @@ -260,27 +260,27 @@ protected override void Dispose(bool disposing) private void CloseStreamFromDispose(bool disposing) { - try + // Dispose of our resources if this StreamWriter is closable. + if (_closable && !_disposed) { - // Dispose of our resources if this StreamReader is closable. - if (_closable && !_disposed) + try { - // Attempt to close the stream - // Note that Stream.Close() can potentially throw here. - // In this case, we still need to ensure + // Attempt to close the stream even if there was an IO error from Flushing. + // Note that Stream.Close() can potentially throw here (may or may not be + // due to the same Flush error). In this case, we still need to ensure // cleaning up internal resources, hence the finally block. if (disposing) { _stream.Close(); } } - } - finally - { - _disposed = true; - _charPos = 0; - _charLen = 0; - base.Dispose(disposing); + finally + { + _disposed = true; + _charPos = 0; + _charLen = 0; + base.Dispose(disposing); + } } } @@ -289,23 +289,47 @@ public override ValueTask DisposeAsync() => base.DisposeAsync() : DisposeAsyncCore(); - private ValueTask DisposeAsyncCore() + private async ValueTask DisposeAsyncCore() { - // Same logic as in Dispose(). + // Same logic as in StreamWriter.DisposeAsync(). Debug.Assert(GetType() == typeof(StreamReader)); try { if (!_disposed) { + ThrowIfDisposed(); CheckAsyncTaskInProgress(); } } finally { - CloseStreamFromDispose(disposing: true); + await CloseStreamFromDisposeAsync(disposing: true).ConfigureAwait(false); } GC.SuppressFinalize(this); - return ValueTask.CompletedTask; + } + private async ValueTask CloseStreamFromDisposeAsync(bool disposing) + { + // Dispose of our resources if this StreamWriter is closable. + if (_closable && !_disposed) + { + try + { + // Attempt to close the stream even if there was an IO error from Flushing. + // Note that Stream.Close() can potentially throw here (may or may not be + // due to the same Flush error). In this case, we still need to ensure + // cleaning up internal resources, hence the finally block. + if (disposing) + { + await _stream.DisposeAsync().ConfigureAwait(false); + } + } + finally + { + _disposed = true; + _charLen = 0; + await base.DisposeAsync().ConfigureAwait(false); + } + } } public virtual Encoding CurrentEncoding => _encoding; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 779b4f61d5207e..2b22ca377bb853 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -214,10 +214,10 @@ protected override void Dispose(bool disposing) private void CloseStreamFromDispose(bool disposing) { - try + // Dispose of our resources if this StreamWriter is closable. + if (_closable && !_disposed) { - // Dispose of our resources if this StreamWriter is closable. - if (_closable && !_disposed) + try { // Attempt to close the stream even if there was an IO error from Flushing. // Note that Stream.Close() can potentially throw here (may or may not be @@ -228,12 +228,12 @@ private void CloseStreamFromDispose(bool disposing) _stream.Close(); } } - } - finally - { - _disposed = true; - _charLen = 0; - base.Dispose(disposing); + finally + { + _disposed = true; + _charLen = 0; + base.Dispose(disposing); + } } } @@ -255,11 +255,36 @@ private async ValueTask DisposeAsyncCore() } finally { - CloseStreamFromDispose(disposing: true); + await CloseStreamFromDisposeAsync(disposing: true).ConfigureAwait(false); } GC.SuppressFinalize(this); } + private async ValueTask CloseStreamFromDisposeAsync(bool disposing) + { + // Dispose of our resources if this StreamWriter is closable. + if (_closable && !_disposed) + { + try + { + // Attempt to close the stream even if there was an IO error from Flushing. + // Note that Stream.Close() can potentially throw here (may or may not be + // due to the same Flush error). In this case, we still need to ensure + // cleaning up internal resources, hence the finally block. + if (disposing) + { + await _stream.DisposeAsync().ConfigureAwait(false); + } + } + finally + { + _disposed = true; + _charLen = 0; + await base.DisposeAsync().ConfigureAwait(false); + } + } + } + public override void Flush() { CheckAsyncTaskInProgress(); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 958f48681a09db..71430f5c23041f 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10210,6 +10210,7 @@ public StreamReader(string path, System.Text.Encoding encoding, bool detectEncod public override void Close() { } public void DiscardBufferedData() { } protected override void Dispose(bool disposing) { } + public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } public override int Peek() { throw null; } public override int Read() { throw null; } public override int Read(char[] buffer, int index, int count) { throw null; } @@ -10325,13 +10326,14 @@ public override void WriteLine(System.Text.StringBuilder? value) { } public override System.Threading.Tasks.Task WriteLineAsync(string? value) { throw null; } public override System.Threading.Tasks.Task WriteLineAsync(System.Text.StringBuilder? value, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } - public abstract partial class TextReader : System.MarshalByRefObject, System.IDisposable + public abstract partial class TextReader : System.MarshalByRefObject, System.IAsyncDisposable, System.IDisposable { public static readonly System.IO.TextReader Null; protected TextReader() { } public virtual void Close() { } public void Dispose() { } protected virtual void Dispose(bool disposing) { } + public virtual System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } public virtual int Peek() { throw null; } public virtual int Read() { throw null; } public virtual int Read(char[] buffer, int index, int count) { throw null; } From a4e615c586839bf4b651c40386518c69246f01a8 Mon Sep 17 00:00:00 2001 From: dersia Date: Thu, 27 Jul 2023 22:28:29 +0200 Subject: [PATCH 4/4] Fixed some Typos. Removed bool disposing from AsyncMethods. Removed uneccessary call to ThrowIfDisposed --- .../src/System/IO/StreamReader.cs | 13 +++++-------- .../src/System/IO/StreamWriter.cs | 11 ++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 17cd948d04e892..9c294da1668253 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -297,17 +297,16 @@ private async ValueTask DisposeAsyncCore() { if (!_disposed) { - ThrowIfDisposed(); CheckAsyncTaskInProgress(); } } finally { - await CloseStreamFromDisposeAsync(disposing: true).ConfigureAwait(false); + await CloseStreamFromDisposeAsync().ConfigureAwait(false); } GC.SuppressFinalize(this); } - private async ValueTask CloseStreamFromDisposeAsync(bool disposing) + private async ValueTask CloseStreamFromDisposeAsync() { // Dispose of our resources if this StreamWriter is closable. if (_closable && !_disposed) @@ -315,17 +314,15 @@ private async ValueTask CloseStreamFromDisposeAsync(bool disposing) try { // Attempt to close the stream even if there was an IO error from Flushing. - // Note that Stream.Close() can potentially throw here (may or may not be + // Note that Stream.DisposeAsync() can potentially throw here (may or may not be // due to the same Flush error). In this case, we still need to ensure // cleaning up internal resources, hence the finally block. - if (disposing) - { - await _stream.DisposeAsync().ConfigureAwait(false); - } + await _stream.DisposeAsync().ConfigureAwait(false); } finally { _disposed = true; + _charPos = 0; _charLen = 0; await base.DisposeAsync().ConfigureAwait(false); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 2b22ca377bb853..1273d41f7539f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -255,12 +255,12 @@ private async ValueTask DisposeAsyncCore() } finally { - await CloseStreamFromDisposeAsync(disposing: true).ConfigureAwait(false); + await CloseStreamFromDisposeAsync().ConfigureAwait(false); } GC.SuppressFinalize(this); } - private async ValueTask CloseStreamFromDisposeAsync(bool disposing) + private async ValueTask CloseStreamFromDisposeAsync() { // Dispose of our resources if this StreamWriter is closable. if (_closable && !_disposed) @@ -268,13 +268,10 @@ private async ValueTask CloseStreamFromDisposeAsync(bool disposing) try { // Attempt to close the stream even if there was an IO error from Flushing. - // Note that Stream.Close() can potentially throw here (may or may not be + // Note that Stream.DisposeAsync() can potentially throw here (may or may not be // due to the same Flush error). In this case, we still need to ensure // cleaning up internal resources, hence the finally block. - if (disposing) - { - await _stream.DisposeAsync().ConfigureAwait(false); - } + await _stream.DisposeAsync().ConfigureAwait(false); } finally {