From 60dce2450ec10b8211d1899302431f09a52befd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Thu, 20 Dec 2018 01:05:04 +0100 Subject: [PATCH 1/2] Add SemaphoreSlimContext class --- .../Threading/SemaphoreSlimContext.cs | 60 +++++++++ .../Threading/SemaphoreSlimExtensions.cs | 121 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/THNETII.Common/Threading/SemaphoreSlimContext.cs create mode 100644 src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs diff --git a/src/THNETII.Common/Threading/SemaphoreSlimContext.cs b/src/THNETII.Common/Threading/SemaphoreSlimContext.cs new file mode 100644 index 00000000..8f97c93a --- /dev/null +++ b/src/THNETII.Common/Threading/SemaphoreSlimContext.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace THNETII.Common.Threading +{ + public class SemaphoreSlimContext : IDisposable + { + private readonly SemaphoreSlim semaphore; + + protected int ReleaseCount { get; } + + private SemaphoreSlimContext(SemaphoreSlim semaphore, int releaseCount, bool verify) + { + Debug.Assert(!verify, nameof(verify) + " is not false."); + this.semaphore = semaphore; + ReleaseCount = releaseCount; + } + + protected SemaphoreSlimContext(SemaphoreSlim semaphore, + int releaseCount = 1) + : this( + semaphore ?? throw new ArgumentNullException(nameof(semaphore)), + releaseCount >= 1 ? releaseCount : throw new ArgumentOutOfRangeException(nameof(releaseCount), releaseCount, nameof(releaseCount) + " is less than 1"), + verify: false + ) + { } + + internal static SemaphoreSlimContext CreateNoVerify(SemaphoreSlim semaphore, + int releaseCount = 1) => + new SemaphoreSlimContext(semaphore, releaseCount, verify: false); + + #region IDisposable Support + private int disposedValue = 0; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (Interlocked.Exchange(ref disposedValue, 1) == 0) + { + if (disposing) + { + semaphore?.Release(ReleaseCount); + } + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + GC.SuppressFinalize(this); + } + + ~SemaphoreSlimContext() + { + Dispose(false); + } + #endregion + } +} diff --git a/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs b/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs new file mode 100644 index 00000000..1313b3f4 --- /dev/null +++ b/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace THNETII.Common.Threading +{ + public static class SemaphoreSlimExtensions + { + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + semaphore.Wait(); + return SemaphoreSlimContext.CreateNoVerify(semaphore); + } + + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, + int millisecondsTimeout) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = semaphore.Wait(millisecondsTimeout); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, + TimeSpan timeout) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = semaphore.Wait(timeout); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, + CancellationToken cancelToken) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + semaphore.Wait(cancelToken); + return SemaphoreSlimContext.CreateNoVerify(semaphore); + } + + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, + int millisecondsTimeout, CancellationToken cancelToken) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = semaphore.Wait(millisecondsTimeout, cancelToken); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, + TimeSpan timeout, CancellationToken cancelToken) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = semaphore.Wait(timeout, cancelToken); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static async Task WaitContextAsync(this SemaphoreSlim semaphore) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + await semaphore.WaitAsync() + .ConfigureAwait(continueOnCapturedContext: false); + return SemaphoreSlimContext.CreateNoVerify(semaphore); + } + + public static async Task WaitContextAsync(this SemaphoreSlim semaphore, + int millisecondsTimeout) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = await semaphore.WaitAsync(millisecondsTimeout) + .ConfigureAwait(continueOnCapturedContext: false); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static async Task WaitContextAsync(this SemaphoreSlim semaphore, + TimeSpan timeout) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = await semaphore.WaitAsync(timeout) + .ConfigureAwait(continueOnCapturedContext: false); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static async Task WaitContextAsync(this SemaphoreSlim semaphore, + CancellationToken cancelToken) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + await semaphore.WaitAsync(cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + return SemaphoreSlimContext.CreateNoVerify(semaphore); + } + + public static async Task WaitContextAsync(this SemaphoreSlim semaphore, + int millisecondsTimeout, CancellationToken cancelToken) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = await semaphore.WaitAsync(millisecondsTimeout, cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + + public static async Task WaitContextAsync(this SemaphoreSlim semaphore, + TimeSpan timeout, CancellationToken cancelToken) + { + if (semaphore is null) + throw new ArgumentNullException(nameof(semaphore)); + var entered = await semaphore.WaitAsync(timeout, cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; + } + } +} From c4d960b99881e8395523f790758285980477d5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Thu, 20 Dec 2018 12:05:31 +0100 Subject: [PATCH 2/2] Refactor SemaphoreSlimContext and add documentation --- .../Threading/SemaphoreSlimContext.cs | 411 +++++++++++++++++- .../Threading/SemaphoreSlimExtensions.cs | 142 ++---- 2 files changed, 452 insertions(+), 101 deletions(-) diff --git a/src/THNETII.Common/Threading/SemaphoreSlimContext.cs b/src/THNETII.Common/Threading/SemaphoreSlimContext.cs index 8f97c93a..27a87b63 100644 --- a/src/THNETII.Common/Threading/SemaphoreSlimContext.cs +++ b/src/THNETII.Common/Threading/SemaphoreSlimContext.cs @@ -1,22 +1,58 @@ using System; using System.Diagnostics; using System.Threading; +using System.Threading.Tasks; namespace THNETII.Common.Threading { + /// + /// A disposable context token that can be used to track the lifetime during + /// which an application holds access to a + /// instance. + /// + /// When the context is disposed (or garbage collected), the + /// method on the original + /// instance is invoked. + /// + /// + /// + /// Using this class allows developers to wrap a critical section protected + /// by a semaphore in a using code-block. This guarantees for safe + /// resource management similar to the lock code-block. However, in + /// addition to the lock block, the + /// class also supports asynchronous locking through its Async-methods. + /// public class SemaphoreSlimContext : IDisposable { private readonly SemaphoreSlim semaphore; + /// + /// Gets the number of times the object + /// that is captured by the current instance should be released. + /// + /// An value greater than 0 (zero). + /// + /// The returned value corresponds to the value that is passed as the + /// argument value to when + /// is invoked. + /// protected int ReleaseCount { get; } - private SemaphoreSlimContext(SemaphoreSlim semaphore, int releaseCount, bool verify) + private SemaphoreSlimContext(SemaphoreSlim semaphore, int releaseCount = 1, bool verify = false) { Debug.Assert(!verify, nameof(verify) + " is not false."); this.semaphore = semaphore; ReleaseCount = releaseCount; } + /// + /// Creates a new semaphore context for the specified and already + /// acquired instance. + /// + /// The instance that has been acquired by the application. + /// The number of times the semaphore has been acquired. Defaults to 1. + /// is null. + /// is less than 1. protected SemaphoreSlimContext(SemaphoreSlim semaphore, int releaseCount = 1) : this( @@ -30,27 +66,390 @@ internal static SemaphoreSlimContext CreateNoVerify(SemaphoreSlim semaphore, int releaseCount = 1) => new SemaphoreSlimContext(semaphore, releaseCount, verify: false); + /// + /// Blocks the current thread until it can enter the + /// and returns a context object that relases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// + /// A instance that captures + /// and releases the semaphore once its + /// method is invoked. + /// + /// is null. + /// instance has been disposed. + public static SemaphoreSlimContext CreateOnEnter(SemaphoreSlim semaphore) + { + semaphore.ThrowIfNull(nameof(semaphore)).Wait(); + return new SemaphoreSlimContext(semaphore, verify: false); + } + + /// + /// Blocks the current thread until it can enter the , + /// while observing a + /// and returns a context object that relases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// The token to observe. + /// + /// A instance that captures + /// and releases the semaphore once its + /// method is invoked. + /// + /// is null. + /// was canceled. + /// + /// instance has been disposed.
+ /// -or-
+ /// The that created has already been disposed. + ///
+ public static SemaphoreSlimContext CreateOnEnter(SemaphoreSlim semaphore, + CancellationToken cancelToken) + { + semaphore.ThrowIfNull(nameof(semaphore)).Wait(cancelToken); + return new SemaphoreSlimContext(semaphore, verify: false); + } + + /// + /// Blocks the current thread until it can enter the , + /// using a 32-bit integer that specifies the timeout + /// and returns a context object that relases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// + /// null if the timeout expired before the semphore could be entered; otherwise, + /// a instance that captures + /// and releases the semaphore once its + /// method is invoked. + /// + /// is null. + /// is a negative value other than -1, which represents an infinite time-out. + /// instance has been disposed. + public static SemaphoreSlimContext CreateOnEnter(SemaphoreSlim semaphore, + int millisecondsTimeout) + { + bool entered = semaphore.ThrowIfNull(nameof(semaphore)) + .Wait(millisecondsTimeout); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Blocks the current thread until it can enter the , + /// using a 32-bit integer that specifies the timeout, while observing a + /// and returns a context object that relases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// The token to observe. + /// + /// null if the timeout expired before the semphore could be entered; otherwise, + /// a instance that captures + /// and releases the semaphore once its + /// method is invoked. + /// + /// is null. + /// is a negative value other than -1, which represents an infinite time-out. + /// was canceled. + /// + /// instance has been disposed.
+ /// -or-
+ /// The that created has already been disposed. + ///
+ public static SemaphoreSlimContext CreateOnEnter(SemaphoreSlim semaphore, + int millisecondsTimeout, CancellationToken cancelToken) + { + bool entered = semaphore.ThrowIfNull(nameof(semaphore)) + .Wait(millisecondsTimeout, cancelToken); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Blocks the current thread until it can enter the , + /// using the specified timeout + /// and returns a context object that relases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// A value that represents time to wait, or a that represents -1 milliseconds to wait indefinitely. + /// + /// null if the timeout expired before the semphore could be entered; otherwise, + /// a instance that captures + /// and releases the semaphore once its + /// method is invoked. + /// + /// is null. + /// is a negative value other than -1 milliseconds, which represents an infinite time-out. + /// instance has been disposed. + public static SemaphoreSlimContext CreateOnEnter(SemaphoreSlim semaphore, + TimeSpan timeout) + { + bool entered = semaphore.ThrowIfNull(nameof(semaphore)) + .Wait(timeout); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Blocks the current thread until it can enter the , + /// using the specified timeout while observing a + /// and returns a context object that relases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// A value that represents time to wait, or a that represents -1 milliseconds to wait indefinitely. + /// The token to observe. + /// + /// null if the timeout expired before the semphore could be entered; otherwise, + /// a instance that captures + /// and releases the semaphore once its + /// method is invoked. + /// + /// is null. + /// is a negative value other than -1 milliseconds, which represents an infinite time-out. + /// was canceled. + /// + /// instance has been disposed.
+ /// -or-
+ /// The that created has already been disposed. + ///
+ public static SemaphoreSlimContext CreateOnEnter(SemaphoreSlim semaphore, + TimeSpan timeout, CancellationToken cancelToken) + { + bool entered = semaphore.ThrowIfNull(nameof(semaphore)) + .Wait(timeout, cancelToken); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Asynchronously waits to enter the and + /// returns a context object that releases the semaphore when it is + /// disposed. + /// + /// The semaphore to enter. + /// A task that will complete when the semaphore has been entered. + /// is null. + /// has already been disposed. + public static async Task CreateOnEnterAsync( + SemaphoreSlim semaphore) + { + await semaphore.ThrowIfNull(nameof(semaphore)).WaitAsync() + .ConfigureAwait(continueOnCapturedContext: false); + return new SemaphoreSlimContext(semaphore, verify: false); + } + + /// + /// Asynchronously waits to enter a , + /// while observing a and + /// returns a context object that releases the semaphore when it is + /// disposed. + /// + /// A task that will complete when the semaphore has been entered. + /// The semaphore to enter. + /// The token to observe. + /// is null. + /// + /// instance has been disposed.
+ /// -or-
+ /// The that created has already been disposed. + ///
+ /// was canceled. + public static async Task CreateOnEnterAsync( + SemaphoreSlim semaphore, CancellationToken cancelToken) + { + await semaphore.ThrowIfNull(nameof(semaphore)).WaitAsync(cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + return new SemaphoreSlimContext(semaphore, verify: false); + } + + /// + /// Asynchronously waits to enter a , + /// using a 32-bit signed integer to measure the time interval and + /// returns a context object that releases the semaphore when it is + /// disposed. + /// + /// + /// A task that will complete with a result of a context object that + /// releases the semaphore when disposed if the current thread + /// successfully entered the , + /// otherwise with a result of null. + /// The semaphore to enter. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// is null. + /// is a negative number other than -1, which represents an infinite time-out. + /// has already been disposed. + public static async Task CreateOnEnterAsync( + SemaphoreSlim semaphore, int millisecondsTimeout) + { + bool entered = await semaphore.ThrowIfNull(nameof(semaphore)) + .WaitAsync(millisecondsTimeout) + .ConfigureAwait(continueOnCapturedContext: false); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Asynchronously waits to enter a , + /// using a 32-bit signed integer to measure the time interval + /// while observing a and + /// returns a context object that releases the semaphore when it is + /// disposed. + /// + /// + /// A task that will complete with a result of a context object that + /// releases the semaphore when disposed if the current thread + /// successfully entered the , + /// otherwise with a result of null. + /// The semaphore to enter. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// The token to observe. + /// is null. + /// is a negative number other than -1, which represents an infinite time-out. + /// + /// instance has been disposed.
+ /// -or-
+ /// The that created has already been disposed. + ///
+ /// was canceled. + public static async Task CreateOnEnterAsync( + SemaphoreSlim semaphore, int millisecondsTimeout, + CancellationToken cancelToken) + { + bool entered = await semaphore.ThrowIfNull(nameof(semaphore)) + .WaitAsync(millisecondsTimeout, cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Asynchronously waits to enter a , + /// using the specified timeout and + /// returns a context object that releases the semaphore when it is + /// disposed. + /// + /// + /// A task that will complete with a result of a context object that + /// releases the semaphore when disposed if the current thread + /// successfully entered the , + /// otherwise with a result of null. + /// The semaphore to enter. + /// The time to wait, or a value representing -1 milliseconds to wait indefinitely. + /// is null. + /// is a negative value other than -1 milliseconds, which represents an infinite time-out. + /// has already been disposed. + public static async Task CreateOnEnterAsync( + SemaphoreSlim semaphore, TimeSpan timeout) + { + bool entered = await semaphore.ThrowIfNull(nameof(semaphore)) + .WaitAsync(timeout) + .ConfigureAwait(continueOnCapturedContext: false); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + + /// + /// Asynchronously waits to enter a , + /// using the specified timeout and + /// while observing a and + /// returns a context object that releases the semaphore when it is + /// disposed. + /// + /// + /// A task that will complete with a result of a context object that + /// releases the semaphore when disposed if the current thread + /// successfully entered the , + /// otherwise with a result of null. + /// The semaphore to enter. + /// The time to wait, or a value representing -1 milliseconds to wait indefinitely. + /// The token to observe. + /// is null. + /// is a negative value other than -1 milliseconds, which represents an infinite time-out. + /// + /// instance has been disposed.
+ /// -or-
+ /// The that created has already been disposed. + ///
+ /// was canceled. + public static async Task CreateOnEnterAsync( + SemaphoreSlim semaphore, TimeSpan timeout, + CancellationToken cancelToken) + { + bool entered = await semaphore.ThrowIfNull(nameof(semaphore)) + .WaitAsync(timeout, cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + return entered + ? new SemaphoreSlimContext(semaphore, verify: false) + : null; + } + #region IDisposable Support private int disposedValue = 0; // To detect redundant calls + /// + /// Releases the captured semaphore by calling + /// with + /// as the argument value. + /// + /// This method is thread-safe and guaranteed to only release the + /// semaphore exactly once during the lifetime of this context instance. + /// + /// + /// + /// true if called from within an implementation of + /// , false if called from + /// within the finalizer. + /// + /// + /// The method atomically flips a state flag variable when the method is + /// executed the first time. Thus, subsequent calls to + /// have no effect. + /// protected virtual void Dispose(bool disposing) { if (Interlocked.Exchange(ref disposedValue, 1) == 0) { - if (disposing) - { - semaphore?.Release(ReleaseCount); - } + semaphore?.Release(ReleaseCount); } } + /// + /// Releases the semaphore instance. + /// + /// + /// This method is thread-safe. The semaphore is only released once. + /// After the initial call to , subsequent calls + /// have no effect. + /// public void Dispose() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); GC.SuppressFinalize(this); } + /// + /// Releases the semaphore object captured by this instance when this is + /// instance is garbage collected. + /// + /// + /// Applications should not rely on garbage collection to properly + /// release held semaphore objects. Instead the current context instance + /// should be used in a using code-block. + /// ~SemaphoreSlimContext() { Dispose(false); diff --git a/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs b/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs index 1313b3f4..6ba75df0 100644 --- a/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs +++ b/src/THNETII.Common/Threading/SemaphoreSlimExtensions.cs @@ -4,118 +4,70 @@ namespace THNETII.Common.Threading { + /// + /// Provides extension methods for the class. + /// public static class SemaphoreSlimExtensions { - public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - semaphore.Wait(); - return SemaphoreSlimContext.CreateNoVerify(semaphore); - } + /// + public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore) => + SemaphoreSlimContext.CreateOnEnter(semaphore); + /// public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, - int millisecondsTimeout) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = semaphore.Wait(millisecondsTimeout); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + int millisecondsTimeout) => + SemaphoreSlimContext.CreateOnEnter(semaphore, millisecondsTimeout); + /// public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, - TimeSpan timeout) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = semaphore.Wait(timeout); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + TimeSpan timeout) => + SemaphoreSlimContext.CreateOnEnter(semaphore, timeout); + /// public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, - CancellationToken cancelToken) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - semaphore.Wait(cancelToken); - return SemaphoreSlimContext.CreateNoVerify(semaphore); - } + CancellationToken cancelToken) => + SemaphoreSlimContext.CreateOnEnter(semaphore, cancelToken); + /// public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, - int millisecondsTimeout, CancellationToken cancelToken) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = semaphore.Wait(millisecondsTimeout, cancelToken); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + int millisecondsTimeout, CancellationToken cancelToken) => + SemaphoreSlimContext.CreateOnEnter(semaphore, millisecondsTimeout, cancelToken); + /// public static SemaphoreSlimContext WaitContext(this SemaphoreSlim semaphore, - TimeSpan timeout, CancellationToken cancelToken) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = semaphore.Wait(timeout, cancelToken); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + TimeSpan timeout, CancellationToken cancelToken) => + SemaphoreSlimContext.CreateOnEnter(semaphore, timeout, cancelToken); - public static async Task WaitContextAsync(this SemaphoreSlim semaphore) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - await semaphore.WaitAsync() - .ConfigureAwait(continueOnCapturedContext: false); - return SemaphoreSlimContext.CreateNoVerify(semaphore); - } + /// + public static Task WaitContextAsync( + this SemaphoreSlim semaphore) => + SemaphoreSlimContext.CreateOnEnterAsync(semaphore); - public static async Task WaitContextAsync(this SemaphoreSlim semaphore, - int millisecondsTimeout) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = await semaphore.WaitAsync(millisecondsTimeout) - .ConfigureAwait(continueOnCapturedContext: false); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + /// + public static Task WaitContextAsync( + this SemaphoreSlim semaphore, int millisecondsTimeout) => + SemaphoreSlimContext.CreateOnEnterAsync(semaphore, millisecondsTimeout); - public static async Task WaitContextAsync(this SemaphoreSlim semaphore, - TimeSpan timeout) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = await semaphore.WaitAsync(timeout) - .ConfigureAwait(continueOnCapturedContext: false); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + /// + public static Task WaitContextAsync( + this SemaphoreSlim semaphore, TimeSpan timeout) => + SemaphoreSlimContext.CreateOnEnterAsync(semaphore, timeout); - public static async Task WaitContextAsync(this SemaphoreSlim semaphore, - CancellationToken cancelToken) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - await semaphore.WaitAsync(cancelToken) - .ConfigureAwait(continueOnCapturedContext: false); - return SemaphoreSlimContext.CreateNoVerify(semaphore); - } + /// + public static Task WaitContextAsync( + this SemaphoreSlim semaphore, CancellationToken cancelToken) => + SemaphoreSlimContext.CreateOnEnterAsync(semaphore, cancelToken); - public static async Task WaitContextAsync(this SemaphoreSlim semaphore, - int millisecondsTimeout, CancellationToken cancelToken) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = await semaphore.WaitAsync(millisecondsTimeout, cancelToken) - .ConfigureAwait(continueOnCapturedContext: false); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + /// + public static Task WaitContextAsync( + this SemaphoreSlim semaphore, int millisecondsTimeout, + CancellationToken cancelToken) => + SemaphoreSlimContext.CreateOnEnterAsync(semaphore, millisecondsTimeout, cancelToken); - public static async Task WaitContextAsync(this SemaphoreSlim semaphore, - TimeSpan timeout, CancellationToken cancelToken) - { - if (semaphore is null) - throw new ArgumentNullException(nameof(semaphore)); - var entered = await semaphore.WaitAsync(timeout, cancelToken) - .ConfigureAwait(continueOnCapturedContext: false); - return entered ? SemaphoreSlimContext.CreateNoVerify(semaphore) : null; - } + /// + public static Task WaitContextAsync( + this SemaphoreSlim semaphore, TimeSpan timeout, + CancellationToken cancelToken) => + SemaphoreSlimContext.CreateOnEnterAsync(semaphore, timeout, cancelToken); } }