Skip to content

Instantly share code, notes, and snippets.

@ngbrown
Created May 21, 2024 15:48
Show Gist options
  • Save ngbrown/01b767f416b4eafc41fce10f6c08a622 to your computer and use it in GitHub Desktop.
Save ngbrown/01b767f416b4eafc41fce10f6c08a622 to your computer and use it in GitHub Desktop.

Revisions

  1. ngbrown created this gist May 21, 2024.
    51 changes: 51 additions & 0 deletions SemaphoreSlimExtensions.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,51 @@
    public static class SemaphoreSlimExtensions
    {
    /// <summary>
    /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>. Returns an <see cref="IDisposable"/> to be used in <c>using</c>.
    /// </summary>
    /// <param name="semaphore">A <see cref="SemaphoreSlim"/> to lock.</param>
    /// <returns>An <see cref="IDisposable"/> that will release the <see cref="SemaphoreSlim"/> when disposed.</returns>
    public static IDisposable Lock(this SemaphoreSlim semaphore)
    {
    semaphore.Wait();
    return new SemaphoreReleaser(semaphore);
    }

    /// <summary>
    /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, while observing a
    /// <see cref="CancellationToken"/>. Returned task resolves an <see cref="IDisposable"/> to be used in <c>using</c>.
    /// </summary>
    /// <returns>
    /// A task that will complete when the semaphore has been entered.
    /// Task contains An <see cref="IDisposable"/> that will release the <see cref="SemaphoreSlim"/> when disposed.
    /// </returns>
    /// <param name="semaphore">A <see cref="SemaphoreSlim"/> to lock.</param>
    /// <param name="cancellationToken">
    /// The <see cref="CancellationToken"/> token to observe.
    /// </param>
    public static async Task<IDisposable> 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()
    {
    }
    }
    }