Created
May 27, 2026 10:28
-
-
Save chrisfcarroll/dc50b7ab6eac2cd41b6315eb23f23a62 to your computer and use it in GitHub Desktop.
A SemaphoreSlim(1,1) wrapper for use as an async lock in a using statement
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// <summary> | |
| /// A <see cref="SemaphoreSlim"/> wrapper that can be used in a using statement. | |
| /// The wrapped semaphore has initialCount=1, maxCount=1. | |
| /// <code>using(await theUsableSemaphoreSlim.WaitAsync()) | |
| /// { | |
| /// ... access resources ... | |
| /// } | |
| /// </code> | |
| /// <p>Calling <see cref="Dispose"/> on a <see cref="UsableSemaphoreSlim"/> | |
| /// does not dispose the Semaphore, it releases the lock.</p> | |
| /// <p>To dispose the underlying lock, call <see cref="DisposeUnderlyingLock"/>.</p> | |
| /// </summary> | |
| public sealed class UsableSemaphoreSlim : IDisposable | |
| { | |
| readonly SemaphoreSlim inner = new(1,1); | |
| /// <summary> | |
| /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>. | |
| /// </summary> | |
| /// <returns>A task that will complete when the semaphore has been entered.</returns> | |
| /// <exception cref="ObjectDisposedException"> | |
| /// The underlying Semaphore has been disposed. | |
| /// </exception> | |
| public async Task<IDisposable> WaitAsync() | |
| { | |
| await inner.WaitAsync(); | |
| return this; | |
| } | |
| /// <summary> | |
| /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>. | |
| /// </summary> | |
| /// <exception cref="ObjectDisposedException"> | |
| /// The underlying Semaphore has been disposed. | |
| /// </exception> | |
| public IDisposable Wait() | |
| { | |
| inner.Wait(); | |
| return this; | |
| } | |
| public WaitHandle Available => inner.AvailableWaitHandle; | |
| /// <summary> | |
| /// Calls <see cref="SemaphoreSlim.Release()"/> on the underlying <see cref="SemaphoreSlim"/>. | |
| /// Calling this method does not dispose the underlying <see cref="SemaphoreSlim"/>. | |
| /// To do that, call <see cref="DisposeUnderlyingLock"/>. | |
| /// </summary> | |
| /// <exception cref="ObjectDisposedException"> | |
| /// The underlying Semaphore has been disposed. | |
| /// </exception> | |
| /// <exception cref="SemaphoreFullException"> | |
| /// The underlying Semaphore has already reached it's maximum size, presumably | |
| /// because of more calls to <see cref="Dispose"/> than to <see cref="WaitAsync"/> | |
| /// </exception> | |
| public void Dispose() => inner.Release(); | |
| /// <summary> | |
| /// Call this to dispose the underlying lock. | |
| /// </summary> | |
| public void DisposeUnderlyingLock() => inner.Dispose(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment