From 2cffab14f604422ae3c972ce77a02ab048aa008c Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Wed, 30 Nov 2016 15:04:08 +0000 Subject: [PATCH] StringAsTempFile cleans up in a wider range of circumstances (not relying on finalizer running). Helps with #7 but still doesn't cover all cases. --- .../Util/StringAsTempFile.cs | 48 ++++++++++++++++++- .../project.json | 3 +- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs b/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs index 999dfa24f23b..22f4b018f0d9 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs +++ b/src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs @@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.NodeServices public sealed class StringAsTempFile : IDisposable { private bool _disposedValue; + private bool _hasDeletedTempFile; + private object _fileDeletionLock = new object(); /// /// Create a new instance of . @@ -18,6 +20,18 @@ public StringAsTempFile(string content) { FileName = Path.GetTempFileName(); File.WriteAllText(FileName, content); + + // Because .NET finalizers don't reliably run when the process is terminating, also + // add event handlers for other shutdown scenarios. +#if NET451 + AppDomain.CurrentDomain.ProcessExit += HandleProcessExit; + AppDomain.CurrentDomain.DomainUnload += HandleProcessExit; +#else + // Note that this still doesn't capture SIGKILL (at least on macOS) - there doesn't + // appear to be a way of doing that. So in that case, the temporary file will be + // left behind. + System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += HandleAssemblyUnloading; +#endif } /// @@ -40,15 +54,45 @@ private void DisposeImpl(bool disposing) { if (disposing) { - // Would dispose managed state here, if there was any + // Dispose managed state +#if NET451 + AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit; + AppDomain.CurrentDomain.DomainUnload -= HandleProcessExit; +#else + System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading; +#endif } - File.Delete(FileName); + EnsureTempFileDeleted(); _disposedValue = true; } } + private void EnsureTempFileDeleted() + { + lock (_fileDeletionLock) + { + if (!_hasDeletedTempFile) + { + File.Delete(FileName); + _hasDeletedTempFile = true; + } + } + } + +#if NET451 + private void HandleProcessExit(object sender, EventArgs args) + { + EnsureTempFileDeleted(); + } +#else + private void HandleAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context) + { + EnsureTempFileDeleted(); + } +#endif + /// /// Implements the finalization part of the IDisposable pattern by calling Dispose(false). /// diff --git a/src/Microsoft.AspNetCore.NodeServices/project.json b/src/Microsoft.AspNetCore.NodeServices/project.json index c5eb6af86324..8c917c6fd0ac 100644 --- a/src/Microsoft.AspNetCore.NodeServices/project.json +++ b/src/Microsoft.AspNetCore.NodeServices/project.json @@ -31,7 +31,8 @@ "netstandard1.6": { "dependencies": { "System.Diagnostics.Process": "4.3.0", - "System.IO.FileSystem.Watcher": "4.3.0" + "System.IO.FileSystem.Watcher": "4.3.0", + "System.Runtime.Loader": "4.3.0" } } },