1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using System . Diagnostics ;
3
4
using System . IO ;
4
5
using System . Threading ;
@@ -11,21 +12,22 @@ namespace Umbraco.Cms.Infrastructure.Runtime
11
12
{
12
13
internal class FileSystemMainDomLock : IMainDomLock
13
14
{
14
- private readonly ILogger < FileSystemMainDomLock > _log ;
15
-
15
+ private readonly ILogger < FileSystemMainDomLock > _logger ;
16
16
private readonly CancellationTokenSource _cancellationTokenSource = new ( ) ;
17
-
18
17
private readonly string _lockFilePath ;
19
18
private readonly string _releaseSignalFilePath ;
20
19
21
20
private FileStream _lockFileStream ;
21
+ private Task _listenForReleaseSignalFileTask ;
22
+
23
+ private const int s_maxTriesRemovingLockReleaseSignalFile = 3 ;
22
24
23
25
public FileSystemMainDomLock (
24
- ILogger < FileSystemMainDomLock > log ,
26
+ ILogger < FileSystemMainDomLock > logger ,
25
27
IMainDomKeyGenerator mainDomKeyGenerator ,
26
28
IHostingEnvironment hostingEnvironment )
27
29
{
28
- _log = log ;
30
+ _logger = logger ;
29
31
30
32
var lockFileName = $ "MainDom_{ mainDomKeyGenerator . GenerateKey ( ) } .lock";
31
33
_lockFilePath = Path . Combine ( hostingEnvironment . LocalTempPath , lockFileName ) ;
@@ -41,20 +43,20 @@ public Task<bool> AcquireLockAsync(int millisecondsTimeout)
41
43
{
42
44
try
43
45
{
44
- _log . LogDebug ( "Attempting to obtain MainDom lock file handle {lockFilePath}" , _lockFilePath ) ;
46
+ _logger . LogDebug ( "Attempting to obtain MainDom lock file handle {lockFilePath}" , _lockFilePath ) ;
45
47
_lockFileStream = File . Open ( _lockFilePath , FileMode . OpenOrCreate , FileAccess . ReadWrite , FileShare . None ) ;
46
48
DeleteLockReleaseFile ( ) ;
47
49
return Task . FromResult ( true ) ;
48
50
}
49
51
catch ( IOException )
50
52
{
51
- _log . LogDebug ( "Couldn't obtain MainDom lock file handle, signalling for release of {lockFilePath}" , _lockFilePath ) ;
53
+ _logger . LogDebug ( "Couldn't obtain MainDom lock file handle, signalling for release of {lockFilePath}" , _lockFilePath ) ;
52
54
CreateLockReleaseFile ( ) ;
53
55
Thread . Sleep ( 500 ) ;
54
56
}
55
57
catch ( Exception ex )
56
58
{
57
- _log . LogError ( ex , "Unexpected exception attempting to obtain MainDom lock file handle {lockFilePath}, giving up" , _lockFilePath ) ;
59
+ _logger . LogError ( ex , "Unexpected exception attempting to obtain MainDom lock file handle {lockFilePath}, giving up" , _lockFilePath ) ;
58
60
return Task . FromResult ( false ) ;
59
61
}
60
62
}
@@ -64,13 +66,22 @@ public Task<bool> AcquireLockAsync(int millisecondsTimeout)
64
66
}
65
67
66
68
// Create a long running task to poll to check if anyone has created a lock release file.
67
- public Task ListenAsync ( ) =>
68
- Task . Factory . StartNew (
69
+ public Task ListenAsync ( )
70
+ {
71
+ if ( _listenForReleaseSignalFileTask != null )
72
+ {
73
+ return _listenForReleaseSignalFileTask ;
74
+ }
75
+
76
+ _listenForReleaseSignalFileTask = Task . Factory . StartNew (
69
77
ListeningLoop ,
70
78
_cancellationTokenSource . Token ,
71
79
TaskCreationOptions . LongRunning ,
72
80
TaskScheduler . Default ) ;
73
81
82
+ return _listenForReleaseSignalFileTask ;
83
+ }
84
+
74
85
public void Dispose ( )
75
86
{
76
87
_lockFileStream ? . Close ( ) ;
@@ -86,24 +97,29 @@ private void CreateLockReleaseFile()
86
97
}
87
98
catch ( Exception ex )
88
99
{
89
- _log . LogError ( ex , "Unexpected exception attempting to create lock release signal file {file}" , _releaseSignalFilePath ) ;
100
+ _logger . LogError ( ex , "Unexpected exception attempting to create lock release signal file {file}" , _releaseSignalFilePath ) ;
90
101
}
91
102
}
92
103
93
104
private void DeleteLockReleaseFile ( )
94
105
{
95
- while ( File . Exists ( _releaseSignalFilePath ) )
106
+ List < Exception > encounteredExceptions = new ( ) ;
107
+ for ( var i = 0 ; i < s_maxTriesRemovingLockReleaseSignalFile ; i ++ )
96
108
{
97
109
try
98
110
{
99
111
File . Delete ( _releaseSignalFilePath ) ;
112
+ return ;
100
113
}
101
114
catch ( Exception ex )
102
115
{
103
- _log . LogError ( ex , "Unexpected exception attempting to delete release signal file {file}" , _releaseSignalFilePath ) ;
104
- Thread . Sleep ( 500 ) ;
116
+ _logger . LogError ( ex , "Unexpected exception attempting to delete release signal file {file}" , _releaseSignalFilePath ) ;
117
+ encounteredExceptions . Add ( ex ) ;
118
+ Thread . Sleep ( 500 * ( i + 1 ) ) ;
105
119
}
106
120
}
121
+
122
+ throw new ApplicationException ( $ "Failed to remove lock release signal file { _releaseSignalFilePath } ", new AggregateException ( encounteredExceptions ) ) ;
107
123
}
108
124
109
125
private void ListeningLoop ( )
@@ -112,13 +128,13 @@ private void ListeningLoop()
112
128
{
113
129
if ( _cancellationTokenSource . IsCancellationRequested )
114
130
{
115
- _log . LogDebug ( "ListenAsync Task canceled, exiting loop" ) ;
131
+ _logger . LogDebug ( "ListenAsync Task canceled, exiting loop" ) ;
116
132
return ;
117
133
}
118
134
119
135
if ( File . Exists ( _releaseSignalFilePath ) )
120
136
{
121
- _log . LogDebug ( "Found lock release signal file, releasing lock on {lockFilePath}" , _lockFilePath ) ;
137
+ _logger . LogDebug ( "Found lock release signal file, releasing lock on {lockFilePath}" , _lockFilePath ) ;
122
138
_lockFileStream ? . Close ( ) ;
123
139
_lockFileStream = null ;
124
140
break ;
0 commit comments