55using System ;
66using System . Collections . Generic ;
77using System . IO ;
8+ using System . Threading ;
89using Newtonsoft . Json ;
910using NuGetCredentialProvider . Logging ;
1011
@@ -15,20 +16,63 @@ public class SessionTokenCache : ICache<Uri, string>
1516 private static readonly object FileLock = new object ( ) ;
1617 private readonly string cacheFilePath ;
1718 private ILogger logger ;
19+ private CancellationToken cancellationToken ;
20+ private readonly string mutexName ;
1821
19- public SessionTokenCache ( string cacheFilePath , ILogger logger )
22+ public SessionTokenCache ( string cacheFilePath , ILogger logger , CancellationToken cancellationToken )
2023 {
2124 this . cacheFilePath = cacheFilePath ;
2225 this . logger = logger ;
26+ this . mutexName = @"Global\" + cacheFilePath . Replace ( Path . DirectorySeparatorChar , '_' ) ;
2327 }
2428
2529 private Dictionary < Uri , string > Cache
2630 {
2731 get
2832 {
29- lock ( FileLock )
33+ bool mutexHeld = false , dummy ;
34+ using ( Mutex mutex = new Mutex ( false , mutexName , out dummy ) )
3035 {
31- return Deserialize ( ReadFileBytes ( ) ) ;
36+ try
37+ {
38+ try
39+ {
40+ if ( ! mutex . WaitOne ( 0 ) )
41+ {
42+ // We couldn't get the mutex on our first acquisition attempt. Log this so the user knows what we're
43+ // waiting on.
44+ logger . Verbose ( Resources . SessionTokenCacheMutexMiss ) ;
45+
46+ int index = WaitHandle . WaitAny ( new WaitHandle [ ] { mutex , this . cancellationToken . WaitHandle } , - 1 ) ;
47+
48+ if ( index == 1 )
49+ {
50+ logger . Verbose ( Resources . CancelMessage ) ;
51+ return new Dictionary < Uri , string > ( ) ;
52+ }
53+ else if ( index == WaitHandle . WaitTimeout )
54+ {
55+ logger . Verbose ( Resources . SessionTokenCacheMutexFail ) ;
56+ return new Dictionary < Uri , string > ( ) ;
57+ }
58+ }
59+ }
60+ catch ( AbandonedMutexException )
61+ {
62+ // If this is thrown, then we hold the mutex.
63+ }
64+
65+ mutexHeld = true ;
66+
67+ return Deserialize ( ReadFileBytes ( ) ) ;
68+ }
69+ finally
70+ {
71+ if ( mutexHeld )
72+ {
73+ mutex . ReleaseMutex ( ) ;
74+ }
75+ }
3276 }
3377 }
3478 }
@@ -38,11 +82,49 @@ public string this[Uri key]
3882 get => Cache [ key ] ;
3983 set
4084 {
41- lock ( FileLock )
85+ bool mutexHeld = false , dummy ;
86+ using ( Mutex mutex = new Mutex ( false , mutexName , out dummy ) )
4287 {
43- var cache = Cache ;
44- cache [ key ] = value ;
45- WriteFileBytes ( Serialize ( cache ) ) ;
88+ try
89+ {
90+ try
91+ {
92+ if ( ! mutex . WaitOne ( 0 ) )
93+ {
94+ // We couldn't get the mutex on our first acquisition attempt. Log this so the user knows what we're
95+ // waiting on.
96+ logger . Verbose ( Resources . SessionTokenCacheMutexMiss ) ;
97+
98+ int index = WaitHandle . WaitAny ( new WaitHandle [ ] { mutex , this . cancellationToken . WaitHandle } , - 1 ) ;
99+
100+ if ( index == 1 )
101+ {
102+ logger . Verbose ( Resources . CancelMessage ) ;
103+ }
104+ else if ( index == WaitHandle . WaitTimeout )
105+ {
106+ logger . Verbose ( Resources . SessionTokenCacheMutexFail ) ;
107+ }
108+ }
109+ }
110+ catch ( AbandonedMutexException )
111+ {
112+ // If this is thrown, then we hold the mutex.
113+ }
114+
115+ mutexHeld = true ;
116+
117+ var cache = Cache ;
118+ cache [ key ] = value ;
119+ WriteFileBytes ( Serialize ( cache ) ) ;
120+ }
121+ finally
122+ {
123+ if ( mutexHeld )
124+ {
125+ mutex . ReleaseMutex ( ) ;
126+ }
127+ }
46128 }
47129 }
48130 }
@@ -75,11 +157,49 @@ public bool TryGetValue(Uri key, out string value)
75157
76158 public void Remove ( Uri key )
77159 {
78- lock ( FileLock )
160+ bool mutexHeld = false , dummy ;
161+ using ( Mutex mutex = new Mutex ( false , mutexName , out dummy ) )
79162 {
80- var cache = Cache ;
81- cache . Remove ( key ) ;
82- WriteFileBytes ( Serialize ( cache ) ) ;
163+ try
164+ {
165+ try
166+ {
167+ if ( ! mutex . WaitOne ( 0 ) )
168+ {
169+ // We couldn't get the mutex on our first acquisition attempt. Log this so the user knows what we're
170+ // waiting on.
171+ logger . Verbose ( Resources . SessionTokenCacheMutexMiss ) ;
172+
173+ int index = WaitHandle . WaitAny ( new WaitHandle [ ] { mutex , this . cancellationToken . WaitHandle } , - 1 ) ;
174+
175+ if ( index == 1 )
176+ {
177+ logger . Verbose ( Resources . CancelMessage ) ;
178+ }
179+ else if ( index == WaitHandle . WaitTimeout )
180+ {
181+ logger . Verbose ( Resources . SessionTokenCacheMutexFail ) ;
182+ }
183+ }
184+ }
185+ catch ( AbandonedMutexException )
186+ {
187+ // If this is thrown, then we hold the mutex.
188+ }
189+
190+ mutexHeld = true ;
191+
192+ var cache = Cache ;
193+ cache . Remove ( key ) ;
194+ WriteFileBytes ( Serialize ( cache ) ) ;
195+ }
196+ finally
197+ {
198+ if ( mutexHeld )
199+ {
200+ mutex . ReleaseMutex ( ) ;
201+ }
202+ }
83203 }
84204 }
85205
0 commit comments