public void ExecuteAsync_Contested(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { var firstEntered = new AsyncManualResetEvent(); var firstRelease = new AsyncManualResetEvent(); var secondEntered = new AsyncManualResetEvent(); var firstOperation = this.semaphore.ExecuteAsync( async delegate { firstEntered.Set(); await firstRelease; }, this.TimeoutToken); var secondOperation = this.semaphore.ExecuteAsync( delegate { secondEntered.Set(); return(TplExtensions.CompletedTask); }, this.TimeoutToken); await firstEntered.WaitAsync().WithCancellation(this.TimeoutToken); await Assert.ThrowsAsync <TimeoutException>(() => secondEntered.WaitAsync().WithTimeout(ExpectedTimeout)); firstRelease.Set(); await secondEntered.WaitAsync().WithCancellation(this.TimeoutToken); await Task.WhenAll(firstOperation, secondOperation); }); }
public void ExecuteAsync_NullDelegate(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { await Assert.ThrowsAsync <ArgumentNullException>(() => this.semaphore.ExecuteAsync(null !, this.TimeoutToken)); });
public void SuppressRelevance(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { Task unrelatedUser = null; await this.semaphore.ExecuteAsync(async delegate { Assert.Equal(0, this.semaphore.CurrentCount); using (this.semaphore.SuppressRelevance()) { unrelatedUser = this.semaphore.ExecuteAsync(() => TplExtensions.CompletedTask); } await Assert.ThrowsAsync <TimeoutException>(() => unrelatedUser.WithTimeout(ExpectedTimeout)); if (IsReentrantMode(mode)) { await this.semaphore.ExecuteAsync(() => TplExtensions.CompletedTask, this.TimeoutToken); } }); await unrelatedUser.WithCancellation(this.TimeoutToken); Assert.Equal(1, this.semaphore.CurrentCount); }); }
public void ExecuteAsync_OfT_InvokesDelegateInOriginalContext_WithContention(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); int originalThreadId = Environment.CurrentManagedThreadId; this.ExecuteOnDispatcher(async delegate { var releaseHolder = new AsyncManualResetEvent(); var holder = this.semaphore.ExecuteAsync(() => releaseHolder.WaitAsync()); bool executed = false; var waiter = this.semaphore.ExecuteAsync( delegate { Assert.Equal(originalThreadId, Environment.CurrentManagedThreadId); executed = true; return(new ValueTask <int>(5)); }, this.TimeoutToken); releaseHolder.Set(); int result = await waiter.AsTask().WithCancellation(this.TimeoutToken); Assert.Equal(5, result); Assert.True(executed); }); }
public void SemaphoreOwnershipDoesNotResurrect(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); AsyncManualResetEvent releaseInheritor = new AsyncManualResetEvent(); this.ExecuteOnDispatcher(async delegate { Task innerOperation = null; await this.semaphore.ExecuteAsync(delegate { innerOperation = SemaphoreRecycler(); return(TplExtensions.CompletedTask); }); await this.semaphore.ExecuteAsync(async delegate { releaseInheritor.Set(); await Assert.ThrowsAnyAsync <OperationCanceledException>(() => innerOperation); }); }); async Task SemaphoreRecycler() { await releaseInheritor; Assert.Equal(0, this.semaphore.CurrentCount); // Try to enter the semaphore. This should timeout because someone else is holding the semaphore, waiting for us to timeout. await this.semaphore.ExecuteAsync( () => TplExtensions.CompletedTask, new CancellationTokenSource(ExpectedTimeout).Token); } }
public void Semaphore_PreCanceledRequest_DoesNotEnterSemaphore(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { await Assert.ThrowsAnyAsync <OperationCanceledException>(() => this.semaphore.ExecuteAsync(() => TplExtensions.CompletedTask, new CancellationToken(true))); }); }
public void NoDeadlockOnSyncBlockingOnSemaphore_NoContention(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(delegate { this.semaphore.ExecuteAsync(() => Task.CompletedTask).Wait(this.TimeoutToken); return(Task.CompletedTask); }); }
public void DisposeWhileHoldingSemaphore(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { await this.semaphore.ExecuteAsync(delegate { this.semaphore.Dispose(); return(TplExtensions.CompletedTask); }); }); }
protected override ReentrantSemaphore CreateSemaphore(ReentrantSemaphore.ReentrancyMode mode, int initialCount = 1) { if (this.joinableTaskContext == null) { using (this.Dispatcher.Apply()) { this.joinableTaskContext = new JoinableTaskContext(); } } return(ReentrantSemaphore.Create(initialCount, this.joinableTaskContext, mode)); }
public void SemaphoreDoesNotDeadlockReturningToMainThread(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher( async() => { var semaphoreAcquired = new AsyncManualResetEvent(); var continueFirstOperation = new AsyncManualResetEvent(); // First operation holds the semaphore while the next operations are enqueued. var firstOperation = Task.Run( async() => { await this.semaphore.ExecuteAsync( async() => { semaphoreAcquired.Set(); await continueFirstOperation.WaitAsync(); }, this.TimeoutToken); }); await semaphoreAcquired.WaitAsync().WithCancellation(this.TimeoutToken); // We have 3 semaphore requests on the main thread here. // 1. Async request within a JTF.RunAsync // 2. Async request not in a JTF.RunAsync // 3. Sync request. // The goal is to test that the 3rd, sync request, will release the UI thread // to the two async requests, then it will resume its operations. await this.joinableTaskContext.Factory.SwitchToMainThreadAsync(); var secondOperation = this.joinableTaskContext.Factory.RunAsync(() => this.AcquireSemaphoreAsync(this.TimeoutToken)); var thirdOperation = this.AcquireSemaphoreAsync(this.TimeoutToken); bool finalSemaphoreAcquired = this.joinableTaskContext.Factory.Run( () => { var semaphoreTask = this.AcquireSemaphoreAsync(this.TimeoutToken); continueFirstOperation.Set(); return(semaphoreTask); }); await Task.WhenAll(firstOperation, secondOperation.JoinAsync(), thirdOperation).WithCancellation(this.TimeoutToken); Assert.True(secondOperation.Task.GetAwaiter().GetResult()); Assert.True(thirdOperation.GetAwaiter().GetResult()); Assert.True(finalSemaphoreAcquired); }); }
public void SemaphoreWaiterJoinsSemaphoreHolders(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { var firstEntered = new AsyncManualResetEvent(); bool firstOperationReachedMainThread = false; var firstOperation = Task.Run(async delegate { await this.semaphore.ExecuteAsync( async delegate { firstEntered.Set(); await this.joinableTaskContext !.Factory.SwitchToMainThreadAsync(this.TimeoutToken); firstOperationReachedMainThread = true; },
public void CancellationAbortsContendedRequest(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { var release = new AsyncManualResetEvent(); var holder = this.semaphore.ExecuteAsync(() => release.WaitAsync(), this.TimeoutToken); var cts = CancellationTokenSource.CreateLinkedTokenSource(this.TimeoutToken); var waiter = this.semaphore.ExecuteAsync(() => TplExtensions.CompletedTask, cts.Token); Assert.False(waiter.IsCompleted); cts.Cancel(); await Assert.ThrowsAnyAsync <OperationCanceledException>(() => waiter).WithCancellation(this.TimeoutToken); release.Set(); await holder; }); }
public void Reentrant(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { await this.semaphore.ExecuteAsync(async delegate { await this.semaphore.ExecuteAsync(async delegate { await this.semaphore.ExecuteAsync(delegate { return(TplExtensions.CompletedTask); }, this.TimeoutToken); }, this.TimeoutToken); }, this.TimeoutToken); }); }
public void ExecuteAsync_InvokesDelegateInOriginalContext_NoContention(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); int originalThreadId = Environment.CurrentManagedThreadId; this.ExecuteOnDispatcher(async delegate { bool executed = false; await this.semaphore.ExecuteAsync( delegate { Assert.Equal(originalThreadId, Environment.CurrentManagedThreadId); executed = true; return(TplExtensions.CompletedTask); }, this.TimeoutToken); Assert.True(executed); }); }
public void SemaphoreWorkThrows_DoesNotAbandonSemaphore(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { await Assert.ThrowsAsync <InvalidOperationException>(async delegate { await this.semaphore.ExecuteAsync( delegate { throw new InvalidOperationException(); }, this.TimeoutToken); }); await this.semaphore.ExecuteAsync(() => TplExtensions.CompletedTask, this.TimeoutToken); }); }
public void SemaphoreAwaitersAreQueued(ReentrantSemaphore.ReentrancyMode mode) { this.ExecuteOnDispatcher(async delegate { this.semaphore = this.CreateSemaphore(mode); var releaseFirstHolder = new AsyncManualResetEvent(); var holder = this.semaphore.ExecuteAsync(() => releaseFirstHolder.WaitAsync()); const int waiterCount = 5; var cts = new CancellationTokenSource[waiterCount]; var waiters = new Task[waiterCount]; var enteredLog = new List <int>(); for (int i = 0; i < waiterCount; i++) { cts[i] = new CancellationTokenSource(); int j = i; waiters[i] = this.semaphore.ExecuteAsync( () => { enteredLog.Add(j); return(TplExtensions.CompletedTask); }, cts[i].Token); } Assert.All(waiters, waiter => Assert.False(waiter.IsCompleted)); const int canceledWaiterIndex = 2; cts[canceledWaiterIndex].Cancel(); await Assert.ThrowsAnyAsync <OperationCanceledException>(() => waiters[canceledWaiterIndex]).WithCancellation(this.TimeoutToken); Assert.Empty(enteredLog); for (int i = 0; i < waiterCount; i++) { Assert.Equal(i == canceledWaiterIndex, waiters[i].IsCompleted); } // Release the first semaphore. releaseFirstHolder.Set(); // Confirm that the rest streamed through, in the right order. await Assert.ThrowsAnyAsync <OperationCanceledException>(() => Task.WhenAll(waiters)).WithCancellation(this.TimeoutToken); Assert.Equal(Enumerable.Range(0, waiterCount).Except(new[] { canceledWaiterIndex }), enteredLog); }); }
public void Nested_StackStyle(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { await this.semaphore.ExecuteAsync(async delegate { await this.semaphore.ExecuteAsync(async delegate { await Task.Yield(); Assert.Equal(0, this.semaphore.CurrentCount); }); Assert.Equal(0, this.semaphore.CurrentCount); }); Assert.Equal(1, this.semaphore.CurrentCount); }); }
public void SemaphoreWaiterJoinsSemaphoreHolders(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher(async delegate { var firstEntered = new AsyncManualResetEvent(); bool firstOperationReachedMainThread = false; var firstOperation = Task.Run(async delegate { await this.semaphore.ExecuteAsync( async delegate { firstEntered.Set(); await this.joinableTaskContext.Factory.SwitchToMainThreadAsync(this.TimeoutToken); firstOperationReachedMainThread = true; }, this.TimeoutToken); }); bool secondEntryComplete = false; this.joinableTaskContext.Factory.Run(async delegate { await firstEntered.WaitAsync().WithCancellation(this.TimeoutToken); Assumes.False(firstOperationReachedMainThread); // While blocking the main thread, request the semaphore. // This should NOT deadlock if the semaphore properly Joins the existing semaphore holder(s), // allowing them to get to the UI thread and then finally to exit the semaphore so we can enter it. await this.semaphore.ExecuteAsync( delegate { secondEntryComplete = true; Assert.True(firstOperationReachedMainThread); return(TplExtensions.CompletedTask); }, this.TimeoutToken); }); await Task.WhenAll(firstOperation).WithCancellation(this.TimeoutToken); Assert.True(secondEntryComplete); }); }
public void SwitchBackToMainThreadCancels(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); this.ExecuteOnDispatcher( async() => { var release1 = new AsyncManualResetEvent(); var release2 = new AsyncManualResetEvent(); var operation1 = Task.Run( () => this.semaphore.ExecuteAsync( async() => { release1.Set(); await release2; })); using (var abortSemaphore = new CancellationTokenSource()) { await release1; var operation2 = this.AcquireSemaphoreAsync(abortSemaphore.Token); this.joinableTaskContext.Factory.Run( async() => { release2.Set(); await operation1; Assert.Equal(0, this.semaphore.CurrentCount); Assert.False(operation2.IsCompleted); abortSemaphore.Cancel(); await Assert.ThrowsAnyAsync <OperationCanceledException>(() => operation2); Assert.True(await this.AcquireSemaphoreAsync(this.TimeoutToken)); }); } }); }
private static bool IsReentrantMode(ReentrantSemaphore.ReentrancyMode mode) => mode == ReentrantSemaphore.ReentrancyMode.Freeform || mode == ReentrantSemaphore.ReentrancyMode.Stack;
#pragma warning disable VSTHRD012 // Provide JoinableTaskFactory where allowed (we do this in the JTF-aware variant of these tests in a derived class.) protected virtual ReentrantSemaphore CreateSemaphore(ReentrantSemaphore.ReentrancyMode mode = ReentrantSemaphore.ReentrancyMode.NotAllowed, int initialCount = 1) => ReentrantSemaphore.Create(initialCount, mode: mode);