public static class SemaphoreSlimExtensions
{
///
/// Blocks the current thread until it can enter the . Returns an to be used in using.
///
/// A to lock.
/// An that will release the when disposed.
public static IDisposable Lock(this SemaphoreSlim semaphore)
{
semaphore.Wait();
return new SemaphoreReleaser(semaphore);
}
///
/// Asynchronously waits to enter the , while observing a
/// . Returned task resolves an to be used in using.
///
///
/// A task that will complete when the semaphore has been entered.
/// Task contains An that will release the when disposed.
///
/// A to lock.
///
/// The token to observe.
///
public static async Task LockAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken = default)
{
var task = semaphore.WaitAsync(cancellationToken);
await task.ConfigureAwait(false);
return task.Status == TaskStatus.RanToCompletion ? new SemaphoreReleaser(semaphore) : new DisposableNoOp();
}
private sealed class SemaphoreReleaser(SemaphoreSlim semaphore) : IDisposable
{
private bool released;
public void Dispose()
{
if (released) return;
semaphore.Release();
this.released = true;
}
}
private sealed class DisposableNoOp : IDisposable
{
public void Dispose()
{
}
}
}