// 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();
}
}
}
}
}