public async Task TestReleaseThenReAcquireWithConcurrentAccess() { var sut = new FastAsyncLock(); using (var otherThread = new AsyncTestRunner(OtherThread)) { await otherThread.AdvanceTo(1); Assert.IsTrue(otherThread.HasLock()); await otherThread.AdvanceTo(2); Assert.IsFalse(otherThread.HasLock()); using (await sut.LockAsync(CancellationToken.None)) { await Task.Yield(); await otherThread.AdvanceAndFreezeBefore(3); Assert.IsFalse(otherThread.HasLock()); } await otherThread.AdvanceTo(4); Assert.IsTrue(otherThread.HasLock()); } async Task OtherThread(CancellationToken ct, AsyncTestRunner r) { using (await sut.LockAsync(CancellationToken.None)) { await Task.Yield(); r.HasLock(true); r.Sync(position: 1); } await Task.Yield(); r.HasLock(false); r.Sync(position: 2); using (await sut.LockAsync(CancellationToken.None)) { await Task.Yield(); r.HasLock(true); r.Sync(position: 3); r.Sync(position: 4); } await Task.Yield(); r.HasLock(false); } }
public async Task TestExitFromAnotherExecutionContext() { var sut = new FastAsyncLock(); var locking = default(IDisposable); var entryContext = default(AsyncTestContext); using (var externalThread = new AsyncTestRunner(ExternalThread)) { entryContext = AsyncTestContext.Current; locking = await sut.LockAsync(CancellationToken.None); await externalThread.AdvanceTo(1); Assert.IsTrue(externalThread.HasLock()); await externalThread.AdvanceToEnd(); } async Task ExternalThread(CancellationToken ct, AsyncTestRunner r) { Assert.AreNotEqual(entryContext, AsyncTestContext.Current); locking.Dispose(); // Try to relock from the external execution context to validate that the lock was effectively released using (await sut.LockAsync(ct)) { r.HasLock(true); r.Sync(position: 1); await Task.Yield(); } r.HasLock(false); } }
public async Task TestConcurrentCancelFirst() { var entryContext = AsyncTestContext.Current; var sut = new FastAsyncLock(); var ct = new CancellationTokenSource(); using (var otherThread = new AsyncTestRunner(CommonTwoStepsLock(sut))) { // Acquire the lock on another async context await otherThread.Advance(); Assert.IsTrue(otherThread.HasLock()); // Try to acquire the lock from this async context entryContext.Validate(); var locking = sut.LockAsync(CancellationToken.None); Assert.AreEqual(TaskStatus.WaitingForActivation, locking.Status); // Cancel the CT of the other thread ... this should not impact the awaiter ! ct.Cancel(); Assert.AreEqual(TaskStatus.WaitingForActivation, locking.Status); // Finally validate that if we release the lock from the other thread, we are still able to acquire the lock await otherThread.Advance(); Assert.AreEqual(TaskStatus.RanToCompletion, locking.Status); } }
public async Task TestConcurrentAccess() { var entryContext = AsyncTestContext.Current; var sut = new FastAsyncLock(); using (var otherThread = new AsyncTestRunner(CommonTwoStepsLock(sut))) { // Acquire the lock on another async context await otherThread.Advance(); Assert.IsTrue(otherThread.HasLock()); // Try to acquire the lock from this async context entryContext.Validate(); var locking = sut.LockAsync(CancellationToken.None); Assert.AreEqual(TaskStatus.WaitingForActivation, locking.Status); await otherThread.Advance(); // Will release the lock Assert.AreEqual(TaskStatus.RanToCompletion, locking.Status); // so lock is now acquired (sync) } }
public async Task TestConcurrentCancelSecond() { var entryContext = AsyncTestContext.Current; var sut = new FastAsyncLock(); var ct = new CancellationTokenSource(); using (var otherThread = new AsyncTestRunner(CommonTwoStepsLock(sut))) { // Acquire the lock on another async context await otherThread.Advance(); Assert.IsTrue(otherThread.HasLock()); // Try to acquire the lock from this async context entryContext.Validate(); var locking = sut.LockAsync(ct.Token); Assert.AreEqual(TaskStatus.WaitingForActivation, locking.Status); // But cancel before the other async context completes ct.Cancel(); Assert.AreEqual(TaskStatus.Canceled, locking.Status); } }
public async Task TestConcurrentCancelSecondWithThird() { var sut = new FastAsyncLock(); var ct = new CancellationTokenSource(); var thread2LockingTask = default(Task <IDisposable>); using (var thread1 = new AsyncTestRunner(Thread1)) using (var thread2 = new AsyncTestRunner(Thread2)) using (var thread3 = new AsyncTestRunner(Thread3)) { // Acquire the lock on thread 1 THEN await thread1.AdvanceTo(1); Assert.IsTrue(thread1.HasLock()); // Try to acquire the lock from this async context await thread2.AdvanceAndFreezeBefore(1); //var locking = sut.LockAsync(ct.Token); Assert.AreEqual(TaskStatus.WaitingForActivation, thread2LockingTask.Status); Assert.IsFalse(thread2.HasLock()); // Try to acquire it on thread 3 var t3Locked = thread3.AdvanceTo(1); await thread3.IsFrozen(); Assert.IsFalse(thread3.HasLock()); // But cancel before the other async context completes ct.Cancel(); Assert.AreEqual(TaskStatus.Canceled, thread2LockingTask.Status); // Release the lock from thread1, and wait for thread 3 to acquire the lock await thread1.AdvanceAndFreezeBefore(2); // will freeze in continuation of thread 3 await t3Locked; //Assert.IsFalse(thread1.HasLock()); // flag not set yet: the thread 1 is dead locked by the continuation of thread 3 Assert.IsTrue(thread3.HasLock()); await thread3.AdvanceToEnd(); //await thread1.AdvanceToEnd(); await Task.Delay(500); Assert.IsFalse(thread1.HasLock()); Assert.IsFalse(thread3.HasLock()); } async Task Thread1(CancellationToken ct2, AsyncTestRunner r) { using (await sut.LockAsync(ct2)) { r.HasLock(true); r.Sync(position: 1); } r.HasLock(false); r.Sync(position: 2); }; async Task Thread2(CancellationToken ct2, AsyncTestRunner r) { thread2LockingTask = sut.LockAsync(ct.Token); using (await thread2LockingTask) { r.HasLock(true); r.Sync(position: 1); } r.HasLock(false); r.Sync(position: 2); } async Task Thread3(CancellationToken ct2, AsyncTestRunner r) { using (await sut.LockAsync(ct2)) { r.HasLock(true); r.Sync(position: 1); } r.HasLock(false); r.Sync(position: 2); }; }