From 065c7bfa8ef68f65480e274d8e1fb14234ed36b6 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Feb 2018 22:20:00 -0500 Subject: [PATCH] Adding test to verify FileStream.Dispose() ignores IO Related Exception in finalizer (dotnet/corefx#26921) * Adding test to verify FileStream.Dispose() skips IO Related Exception only on finalization * Skipping test on netfx because the behavior in netfx is different Commit migrated from https://github.com/dotnet/corefx/commit/a15cc53eb5a4248c76bdc60beb3e31628bd8acf8 --- .../tests/FileStream/Dispose.cs | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Dispose.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Dispose.cs index 026a82b..edee857 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Dispose.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Dispose.cs @@ -4,6 +4,7 @@ using Microsoft.Win32.SafeHandles; using System; +using System.Diagnostics; using System.IO; using Xunit; @@ -45,6 +46,11 @@ namespace System.IO.Tests : base(path, mode) { } + public MyFileStream(SafeFileHandle handle, FileAccess access, Action disposeMethod) : base(handle, access) + { + DisposeMethod = disposeMethod; + } + public Action DisposeMethod { get; set; } protected override void Dispose(bool disposing) @@ -58,6 +64,92 @@ namespace System.IO.Tests } } + + [Fact] + public void Dispose_CallsVirtualDisposeTrueArg_ThrowsDuringFlushWriteBuffer_DisposeThrows() + { + RemoteInvoke(() => + { + string fileName = GetTestFilePath(); + using (FileStream fscreate = new FileStream(fileName, FileMode.Create)) + { + fscreate.WriteByte(0); + } + bool writeDisposeInvoked = false; + Action writeDisposeMethod = _ => writeDisposeInvoked = true; + using (var fsread = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + Action act = () => // separate method to avoid JIT lifetime-extension issues + { + using (var fswrite = new MyFileStream(fsread.SafeFileHandle, FileAccess.Write, writeDisposeMethod)) + { + fswrite.WriteByte(0); + + // Normal dispose should call Dispose(true). Throws due to FS trying to flush write buffer + Assert.Throws(() => fswrite.Dispose()); + Assert.True(writeDisposeInvoked, "Expected Dispose(true) to be called from Dispose()"); + writeDisposeInvoked = false; + + // Only throws on first Dispose call + fswrite.Dispose(); + Assert.True(writeDisposeInvoked, "Expected Dispose(true) to be called from Dispose()"); + writeDisposeInvoked = false; + } + Assert.True(writeDisposeInvoked, "Expected Dispose(true) to be called from Dispose() again"); + writeDisposeInvoked = false; + }; + act(); + + for (int i = 0; i < 2; i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + Assert.False(writeDisposeInvoked, "Expected finalizer to have been suppressed"); + } + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Missing fix for https://github.com/dotnet/coreclr/pull/16250")] + public void NoDispose_CallsVirtualDisposeFalseArg_ThrowsDuringFlushWriteBuffer_FinalizerWontThrow() + { + RemoteInvoke(() => + { + string fileName = GetTestFilePath(); + using (FileStream fscreate = new FileStream(fileName, FileMode.Create)) + { + fscreate.WriteByte(0); + } + bool writeDisposeInvoked = false; + Action writeDisposeMethod = (disposing) => + { + writeDisposeInvoked = true; + Assert.False(disposing, "Expected false arg to Dispose(bool)"); + }; + using (var fsread = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + Action act = () => // separate method to avoid JIT lifetime-extension issues + { + var fswrite = new MyFileStream(fsread.SafeFileHandle, FileAccess.Write, writeDisposeMethod); + fswrite.WriteByte(0); + }; + act(); + + // Dispose is not getting called here. + // instead, make sure finalizer gets called and doesnt throw exception + for (int i = 0; i < 2; i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + Assert.True(writeDisposeInvoked, "Expected finalizer to be invoked but not throw exception"); + } + return SuccessExitCode; + }).Dispose(); + } + [Fact] public void Dispose_CallsVirtualDispose_TrueArg() { -- 2.7.4