private async static Task WritePayloadToSyncFileAsync(string lockFile, string protectedFile) { string pid = Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture); string errorFile = protectedFile + $"{pid}.e.txt"; string pidFIle = Path.Combine(Path.GetDirectoryName(protectedFile), "finished", pid + ".txt"); Console.WriteLine("Starting process: " + pid); CrossPlatLock crossPlatLock = null; try { crossPlatLock = new CrossPlatLock(lockFile); using (StreamWriter sw = new StreamWriter(protectedFile, true)) { sw.WriteLine($"< {pid} {DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}"); // increase contention by simulating a slow writer await Task.Delay(s_artificialContention).ConfigureAwait(false); sw.WriteLine($"> {pid} {DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}"); Console.WriteLine("Process finished: " + pid); } } catch (Exception e) { File.WriteAllText(errorFile, e.ToString()); throw; } finally { File.WriteAllText(pidFIle, "done"); crossPlatLock.Dispose(); } }
[WorkItem(187)] // https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/issues/187 public void DirNotExists() { // Arrange string cacheFileDir; // ensure the cache directory does not exist do { string tempDirName = System.IO.Path.GetRandomFileName(); cacheFileDir = Path.Combine(Path.GetTempPath(), tempDirName); } while (Directory.Exists(cacheFileDir)); using (var crossPlatLock = new CrossPlatLock( Path.Combine(cacheFileDir, "file.lockfile"), // the directory is guaranteed to not exist 100, 1)) { // no-op } // before fixing the bug, an exception would occur here: // System.InvalidOperationException: Could not get access to the shared lock file. // ---> System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Users\.... }
public static async Task Main(string[] args) { if (args.Length < 2) { PrintUsage(); Console.Read(); return; } string filePath = args[0]; if (!File.Exists(filePath)) { File.Create(filePath); } int delay = int.Parse(args[1], CultureInfo.InvariantCulture); // this object tries to acquire the file lock every 100ms and gives up after 600 attempts (about 1 min) using (var crossPlatLock = new CrossPlatLock(filePath + ".lockfile")) { Console.WriteLine("Acquired the lock..."); Console.WriteLine("Writing..."); File.WriteAllText(filePath, "< " + Process.GetCurrentProcess().Id); Console.WriteLine($"Waiting for {delay}s"); await Task.Delay(delay * 1000).ConfigureAwait(false); Console.WriteLine("Writing..."); File.WriteAllText(filePath, "> " + Process.GetCurrentProcess().Id); } }
// Triggered right after ADAL accessed the cache. // Internal for testing. internal void AfterAccessNotification(TokenCacheNotificationArgs args) { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After access"); try { // if the access operation resulted in a cache update if (HasStateChanged) { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After access, cache in memory HasChanged"); try { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Before Write Store"); byte[] data = SerializeAdalV3(); _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Serializing '{data.Length}' bytes"); _store.WriteData(data); _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After write store"); HasStateChanged = false; } catch (Exception e) { _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An exception was encountered while serializing the {nameof(AdalCache)} : {e}"); _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"No data found in the store, clearing the cache in memory."); // The cache is corrupt clear it out DeserializeAdalV3(null); _store.Clear(); throw; } } } finally { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Releasing lock"); // Get a local copy and call null before disposing because when the lock is disposed the next thread will replace CacheLock with its instance, // therefore we do not want to null out CacheLock after dispose since this may orphan a CacheLock. var localLockCopy = _cacheLock; _cacheLock = null; localLockCopy?.Dispose(); } }
// Triggered right before ADAL needs to access the cache. // Reload the cache from the persistent store in case it changed since the last access. private void BeforeAccessNotification(TokenCacheNotificationArgs args) { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Before access"); _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Acquiring lock for token cache"); _cacheLock = new CrossPlatLock( Path.GetFileNameWithoutExtension(_store.CreationProperties.CacheFileName), Path.Combine(_store.CreationProperties.CacheDirectory, _store.CreationProperties.CacheFileName) + ".lockfile"); if (_store.HasChanged) { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Before access, the store has changed"); byte[] fileData = _store.ReadData(); _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Read '{fileData?.Length}' bytes from storage"); if (fileData != null && fileData.Length > 0) { try { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Deserializing the store"); DeserializeAdalV3(fileData); } catch (Exception e) { _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An exception was encountered while deserializing the {nameof(AdalCache)} : {e}"); _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"No data found in the store, clearing the cache in memory."); // Clear the memory cache DeserializeAdalV3(null); _store.Clear(); throw; } } else { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"No data found in the store, clearing the cache in memory."); // Clear the memory cache DeserializeAdalV3(null); } } }
// Triggered right after ADAL accessed the cache. private void AfterAccessNotification(TokenCacheNotificationArgs args) { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After access"); try { // if the access operation resulted in a cache update if (HasStateChanged) { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After access, cache in memory HasChanged"); try { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Before Write Store"); byte[] data = SerializeAdalV3(); _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Serializing '{data.Length}' bytes"); _store.WriteData(data); _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After write store"); HasStateChanged = false; } catch (Exception e) { _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An exception was encountered while serializing the {nameof(AdalCache)} : {e}"); _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"No data found in the store, clearing the cache in memory."); // The cache is corrupt clear it out DeserializeAdalV3(null); _store.Clear(); throw; } } } finally { _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Releasing lock"); _cacheLock?.Dispose(); _cacheLock = null; } }
// Triggered right before ADAL needs to access the cache. // Reload the cache from the persistent store in case it changed since the last access. // Internal for testing. internal void BeforeAccessNotification(TokenCacheNotificationArgs args) { _logger.LogInformation($"Before access"); _logger.LogInformation($"Acquiring lock for token cache"); _cacheLock = new CrossPlatLock(Path.Combine(_store.CreationProperties.CacheDirectory, _store.CreationProperties.CacheFileName) + ".lockfile", this._lockFileRetryDelay, this._lockFileRetryCount); _logger.LogInformation($"Before access, the store has changed"); byte[] fileData = _store.ReadData(); _logger.LogInformation($"Read '{fileData?.Length}' bytes from storage"); if (fileData != null && fileData.Length > 0) { try { _logger.LogInformation($"Deserializing the store"); DeserializeAdalV3(fileData); } catch (Exception e) { _logger.LogError($"An exception was encountered while deserializing the {nameof(AdalCache)} : {e}"); _logger.LogError($"No data found in the store, clearing the cache in memory."); // Clear the memory cache DeserializeAdalV3(null); _store.Clear(); throw; } } else { _logger.LogInformation($"No data found in the store, clearing the cache in memory."); // Clear the memory cache DeserializeAdalV3(null); } }