Skip to content

Commit 2cffab1

Browse files
StringAsTempFile cleans up in a wider range of circumstances (not relying on finalizer running). Helps with aspnet#7 but still doesn't cover all cases.
1 parent 9001c19 commit 2cffab1

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

src/Microsoft.AspNetCore.NodeServices/Util/StringAsTempFile.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.NodeServices
99
public sealed class StringAsTempFile : IDisposable
1010
{
1111
private bool _disposedValue;
12+
private bool _hasDeletedTempFile;
13+
private object _fileDeletionLock = new object();
1214

1315
/// <summary>
1416
/// Create a new instance of <see cref="StringAsTempFile"/>.
@@ -18,6 +20,18 @@ public StringAsTempFile(string content)
1820
{
1921
FileName = Path.GetTempFileName();
2022
File.WriteAllText(FileName, content);
23+
24+
// Because .NET finalizers don't reliably run when the process is terminating, also
25+
// add event handlers for other shutdown scenarios.
26+
#if NET451
27+
AppDomain.CurrentDomain.ProcessExit += HandleProcessExit;
28+
AppDomain.CurrentDomain.DomainUnload += HandleProcessExit;
29+
#else
30+
// Note that this still doesn't capture SIGKILL (at least on macOS) - there doesn't
31+
// appear to be a way of doing that. So in that case, the temporary file will be
32+
// left behind.
33+
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += HandleAssemblyUnloading;
34+
#endif
2135
}
2236

2337
/// <summary>
@@ -40,15 +54,45 @@ private void DisposeImpl(bool disposing)
4054
{
4155
if (disposing)
4256
{
43-
// Would dispose managed state here, if there was any
57+
// Dispose managed state
58+
#if NET451
59+
AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit;
60+
AppDomain.CurrentDomain.DomainUnload -= HandleProcessExit;
61+
#else
62+
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading;
63+
#endif
4464
}
4565

46-
File.Delete(FileName);
66+
EnsureTempFileDeleted();
4767

4868
_disposedValue = true;
4969
}
5070
}
5171

72+
private void EnsureTempFileDeleted()
73+
{
74+
lock (_fileDeletionLock)
75+
{
76+
if (!_hasDeletedTempFile)
77+
{
78+
File.Delete(FileName);
79+
_hasDeletedTempFile = true;
80+
}
81+
}
82+
}
83+
84+
#if NET451
85+
private void HandleProcessExit(object sender, EventArgs args)
86+
{
87+
EnsureTempFileDeleted();
88+
}
89+
#else
90+
private void HandleAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context)
91+
{
92+
EnsureTempFileDeleted();
93+
}
94+
#endif
95+
5296
/// <summary>
5397
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
5498
/// </summary>

src/Microsoft.AspNetCore.NodeServices/project.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"netstandard1.6": {
3232
"dependencies": {
3333
"System.Diagnostics.Process": "4.3.0",
34-
"System.IO.FileSystem.Watcher": "4.3.0"
34+
"System.IO.FileSystem.Watcher": "4.3.0",
35+
"System.Runtime.Loader": "4.3.0"
3536
}
3637
}
3738
},

0 commit comments

Comments
 (0)