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)));
     });
 }
Пример #7
0
 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);