public void LockTimeoutTest() { // Total of 1000ms delay _storageCreationPropertiesBuilder.CustomizeLockRetry(20, 100); var properties = _storageCreationPropertiesBuilder.Build(); var cache1 = new MockTokenCache(); var helper1 = new MsalCacheHelper( cache1, new MsalCacheStorage(properties, _logger), _logger); var cache2 = new MockTokenCache(); var helper2 = new MsalCacheHelper( cache2, new MsalCacheStorage(properties, _logger), _logger); //Test signalling thread 1 var resetEvent1 = new ManualResetEventSlim(initialState: false); //Test signalling thread 2 var resetEvent2 = new ManualResetEventSlim(initialState: false); //Thread 1 signalling test var resetEvent3 = new ManualResetEventSlim(initialState: false); // Thread 2 signalling test var resetEvent4 = new ManualResetEventSlim(initialState: false); var thread1 = new Thread(() => { var args = new TokenCacheNotificationArgs(cache1, string.Empty, null, false); helper1.BeforeAccessNotification(args); // Indicate we are waiting resetEvent2.Set(); resetEvent1.Wait(); helper1.AfterAccessNotification(args); // Let thread 1 exit resetEvent3.Set(); }); Stopwatch getTime = new Stopwatch(); var thread2 = new Thread(() => { var args = new TokenCacheNotificationArgs(cache2, string.Empty, null, false); getTime.Start(); try { helper2.BeforeAccessNotification(args); } catch (InvalidOperationException) { // Invalid operation is the exception thrown if the lock cannot be acquired getTime.Stop(); } resetEvent1.Set(); }); // Let thread 1 start and get the lock thread1.Start(); // Wait for thread one to get into the lock resetEvent2.Wait(); // Start thread 2 and give it enough time to get blocked on the lock thread2.Start(); // Wait for the seconf thread to finish resetEvent1.Wait(); Assert.IsTrue(getTime.ElapsedMilliseconds > 2000); }