@@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.NodeServices
9
9
public sealed class StringAsTempFile : IDisposable
10
10
{
11
11
private bool _disposedValue ;
12
+ private bool _hasDeletedTempFile ;
13
+ private object _fileDeletionLock = new object ( ) ;
12
14
13
15
/// <summary>
14
16
/// Create a new instance of <see cref="StringAsTempFile"/>.
@@ -18,6 +20,18 @@ public StringAsTempFile(string content)
18
20
{
19
21
FileName = Path . GetTempFileName ( ) ;
20
22
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
21
35
}
22
36
23
37
/// <summary>
@@ -40,15 +54,45 @@ private void DisposeImpl(bool disposing)
40
54
{
41
55
if ( disposing )
42
56
{
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
44
64
}
45
65
46
- File . Delete ( FileName ) ;
66
+ EnsureTempFileDeleted ( ) ;
47
67
48
68
_disposedValue = true ;
49
69
}
50
70
}
51
71
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
+
52
96
/// <summary>
53
97
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
54
98
/// </summary>
0 commit comments