Class ReentrantSemaphore
- Namespace
- Microsoft.VisualStudio.Threading
- Assembly
- Microsoft.VisualStudio.Threading.dll
A JoinableTaskFactory-aware semaphore that allows reentrancy without consuming another slot in the semaphore.
public abstract class ReentrantSemaphore : IDisposable
- Inheritance
-
ReentrantSemaphore
- Implements
- Inherited Members
Properties
CurrentCount
Gets the number of openings that remain in the semaphore.
public int CurrentCount { get; }
Property Value
Methods
Create(int, JoinableTaskContext?, ReentrancyMode)
Initializes a new instance of the ReentrantSemaphore class.
public static ReentrantSemaphore Create(int initialCount = 1, JoinableTaskContext? joinableTaskContext = null, ReentrantSemaphore.ReentrancyMode mode = ReentrancyMode.NotAllowed)
Parameters
initialCount
intThe initial number of concurrent operations to allow.
joinableTaskContext
JoinableTaskContextThe JoinableTaskContext to use to mitigate deadlocks.
mode
ReentrantSemaphore.ReentrancyModeHow to respond to a semaphore request by a caller that has already entered the semaphore.
Returns
Dispose()
Faults all pending semaphore waiters with ObjectDisposedException and rejects all subsequent attempts to enter the semaphore with the same exception.
public void Dispose()
Dispose(bool)
Disposes managed and unmanaged resources held by this instance.
protected virtual void Dispose(bool disposing)
Parameters
ExecuteAsync(Func<Task>, CancellationToken)
Executes a given operation within the semaphore.
public abstract Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken = default)
Parameters
operation
Func<Task>The delegate to invoke once the semaphore is entered. If a JoinableTaskContext was supplied to the constructor, this delegate will execute on the main thread if this is invoked on the main thread, otherwise it will be invoked on the threadpool. When no JoinableTaskContext is supplied to the constructor, this delegate will execute on the caller's context.
cancellationToken
CancellationTokenA cancellation token.
Returns
- Task
A task that completes with the result of
operation
, after the semaphore has been exited.
Exceptions
- InvalidOperationException
Thrown when reentrancy is detected and not allowed based due to NotAllowed being provided to the constructor. This happens when code that already holds the semaphore calls code that attempts to again enter the semaphore. When the called code is not awaited on by the caller, it may be appropriate to suppress this reentrancy detection for the method that is called in a fire-and-forget fashion. To suppress this exception for that specific case while preserving overall protection, use SuppressRelevance().
ExecuteAsync<T>(Func<ValueTask<T>>, CancellationToken)
Executes a given operation within the semaphore.
public abstract ValueTask<T> ExecuteAsync<T>(Func<ValueTask<T>> operation, CancellationToken cancellationToken = default)
Parameters
operation
Func<ValueTask<T>>The delegate to invoke once the semaphore is entered. If a JoinableTaskContext was supplied to the constructor, this delegate will execute on the main thread if this is invoked on the main thread, otherwise it will be invoked on the threadpool. When no JoinableTaskContext is supplied to the constructor, this delegate will execute on the caller's context.
cancellationToken
CancellationTokenA cancellation token.
Returns
- ValueTask<T>
A task that completes with the result of
operation
, after the semaphore has been exited.
Type Parameters
T
The type of value returned by the operation.
Exceptions
- InvalidOperationException
Thrown when reentrancy is detected and not allowed based due to NotAllowed being provided to the constructor. This happens when code that already holds the semaphore calls code that attempts to again enter the semaphore. When the called code is not awaited on by the caller, it may be appropriate to suppress this reentrancy detection for the method that is called in a fire-and-forget fashion. To suppress this exception for that specific case while preserving overall protection, use SuppressRelevance().
SuppressRelevance()
Conceals evidence that the caller has entered this ReentrantSemaphore till its result is disposed.
public virtual ReentrantSemaphore.RevertRelevance SuppressRelevance()
Returns
- ReentrantSemaphore.RevertRelevance
A value to dispose to restore visibility of any presence in this semaphore.
Examples
The following snippet demonstrates a way to use this method.
public async Task DoSomethingAsync()
{
await this.semaphore.ExecuteAsync(async delegate
{
// field access under the semaphore
// ...
await Task.Yield(); // represents some async work
// Fire and forget code that uses the semaphore, but should *not*
// inherit our own posession of the semaphore.
using (this.semaphore.SuppressRelevance())
{
this.DoSomethingLaterAsync().Forget(); // Don't await this, or a deadlock will occur.
}
});
}
private async Task DoSomethingLaterAsync()
{
// This semaphore use will not be seen as nested because of our caller's wrapping
// the call in SuppressRelevance.
// So instead of throwing, it will block till its caller releases the semaphore.
await this.semaphore.ExecuteAsync(async delegate
{
// Whatever
await Task.Yield(); // represents some async work
});
}
Remarks
This method is useful when the caller is about to spin off another operation (e.g. scheduling work to the threadpool) that it does not consider vital to its own completion, in order to prevent the spun off work from abusing the caller's right to the semaphore.
This is a safe call to make whether or not the semaphore is currently held, or whether reentrancy is allowed on this instance.
ThrowIfFaulted()
Throws an exception if this instance has been faulted.
protected virtual void ThrowIfFaulted()