Ejemplo n.º 1
0
            /// <inheritdoc />
            public override async Task ExecuteAsync(Func <Task> operation, CancellationToken cancellationToken = default)
            {
                Requires.NotNull(operation, nameof(operation));
                this.ThrowIfFaulted();

                StrongBox <bool> ownedBox = this.reentrancyDetection.Value;

                if (ownedBox?.Value ?? false)
                {
                    throw Verify.FailOperation("Semaphore is already held and reentrancy setting is '{0}'.", ReentrancyMode.NotAllowed);
                }

                await this.ExecuteCoreAsync(async delegate
                {
                    using (this.joinableTaskCollection?.Join())
                    {
                        AsyncSemaphore.Releaser releaser = await this.semaphore.EnterAsync(cancellationToken).ConfigureAwait(true);
                        try
                        {
                            this.reentrancyDetection.Value = ownedBox = new StrongBox <bool>(true);
                            await operation().ConfigureAwaitRunInline();
                        }
                        finally
                        {
                            // Make it clear to any forks of our ExecutionContexxt that the semaphore is no longer owned.
                            ownedBox.Value = false;
                            DisposeReleaserNoThrow(releaser);
                        }
                    }
                });
            }
Ejemplo n.º 2
0
 /// <summary>
 /// Disposes the specfied release, swallowing certain exceptions.
 /// </summary>
 /// <param name="releaser">The releaser to dispose.</param>
 private static void DisposeReleaserNoThrow(AsyncSemaphore.Releaser releaser)
 {
     try
     {
         releaser.Dispose();
     }
     catch (ObjectDisposedException)
     {
         // Swallow this, since in releasing the semaphore if it's already disposed the caller probably doesn't care.
     }
 }
Ejemplo n.º 3
0
            /// <inheritdoc />
            public override async Task ExecuteAsync(Func <Task> operation, CancellationToken cancellationToken = default)
            {
                Requires.NotNull(operation, nameof(operation));
                this.ThrowIfFaulted();

                // No race condition here: We're accessing AsyncLocal<T> which we by definition have our own copy of.
                // Multiple threads or multiple async methods will all have their own storage for this field.
                Stack <StrongBox <AsyncSemaphore.Releaser> > reentrantStack = this.reentrantCount.Value;

                if (reentrantStack == null || reentrantStack.Count == 0)
                {
                    // When the stack is empty, the semaphore isn't held. But many execution contexts that forked from a common root
                    // would be sharing this same empty Stack<T> instance. If we pushed to that Stack, all those forks would suddenly
                    // be seen as having entered this new top-level semaphore. We therefore allocate a new Stack and assign it to our
                    // AsyncLocal<T> field so that only this particular ExecutionContext is seen as having entered the semaphore.
                    this.reentrantCount.Value = reentrantStack = new Stack <StrongBox <AsyncSemaphore.Releaser> >(capacity: 2);
                }

                await this.ExecuteCoreAsync(async delegate
                {
                    using (this.joinableTaskCollection?.Join())
                    {
                        AsyncSemaphore.Releaser releaser = reentrantStack.Count == 0 ? await this.semaphore.EnterAsync(cancellationToken).ConfigureAwait(true) : default;
                        var pushedReleaser = new StrongBox <AsyncSemaphore.Releaser>(releaser);
                        lock (reentrantStack)
                        {
                            reentrantStack.Push(pushedReleaser);
                        }

                        try
                        {
                            await operation().ConfigureAwaitRunInline();
                        }
                        finally
                        {
                            lock (reentrantStack)
                            {
                                var poppedReleaser = reentrantStack.Pop();
                                if (!object.ReferenceEquals(poppedReleaser, pushedReleaser))
                                {
                                    this.faulted = true;
                                    throw Verify.FailOperation(Strings.SemaphoreStackNestingViolated, ReentrancyMode.Stack);
                                }
                            }

                            DisposeReleaserNoThrow(releaser);
                        }
                    }
                });