// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace System.Reactive.Threading { /// /// Provides an implementation of , enabling mutually exclusive locking /// in async code. /// public sealed class AsyncGate : IAsyncGate, IAsyncGateReleaser { private readonly object _gate = new(); private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly AsyncLocal _recursionCount = new(); /// /// Creates an . /// /// /// This is private because we hope that one day, the .NET runtime will provide a built-in /// asynchronous mutual exclusion primitive, and that we might be able to use that instead of /// our own implementation. Although that might be something we could do by modifying this /// class, it might prove useful to be able to provide the old implementation for backwards /// compatibility, so we don't want AsyncRx.NET consumers to depend on a specific concrete type /// as the implementation. /// private AsyncGate() { } /// /// Creates a new instance of an implementation. /// /// public static IAsyncGate Create() => new AsyncGate(); ValueTask IAsyncGate.AcquireAsync() { var shouldAcquire = false; lock (_gate) { if (_recursionCount.Value == 0) { shouldAcquire = true; _recursionCount.Value = 1; } else { _recursionCount.Value++; } } if (shouldAcquire) { Task acquireTask = _semaphore.WaitAsync(); if (acquireTask.IsCompleted) { return new ValueTask(this); } return new ValueTask(acquireTask.ContinueWith(_ => this)); } return new ValueTask(this); } void IAsyncGateReleaser.Release() { lock (_gate) { Debug.Assert(_recursionCount.Value > 0); if (--_recursionCount.Value == 0) { _semaphore.Release(); } } } } }