Class AsyncBarrier
- Namespace
- CryptoHives.Foundation.Threading.Async.Pooled
- Assembly
- CryptoHives.Foundation.Threading.dll
An async barrier synchronization primitive which uses a pooled approach to implement waiters for ValueTask to reduce memory allocations.
public sealed class AsyncBarrier
- Inheritance
-
AsyncBarrier
- Inherited Members
Remarks
This implementation uses ValueTask for waiters and provides allocation-free async signaling by reusing pooled IValueTaskSource<TResult> instances to avoid allocations of TaskCompletionSource<TResult> and Task.
A barrier synchronizes a fixed number of participants, releasing all of them when all have arrived. After release, the barrier automatically resets for the next phase.
An optional post-phase action can be provided that is executed after all participants have arrived but before they are released. If the post-phase action throws an exception, all participants will receive a BarrierPostPhaseException.
Optional timeout and cancellation token parameters on SignalAndWaitAsync(TimeSpan, CancellationToken).
Important Usage Note: Awaiting on ValueTask has its own caveats, as it is a struct that can only be awaited or converted with AsTask() ONE single time. Additional attempts to await after the first await or additional conversions to AsTask() will throw an InvalidOperationException.
Continuation Scheduling: The RunContinuationAsynchronously property controls how continuations are executed when all participants arrive. When set to true (default), continuations are forced to queue to the thread pool.
Allocation Behavior: Immediate acquisitions are completely allocation-free using atomic
operations. When the barrier is contended, waiting without a timeout is allocation-free on .NET 6.0+
(using UnsafeRegister for cancellation), while older frameworks may allocate for cancellation
registration. Specifying a finite timeout allocates a timer that is automatically disposed when the
operation completes. Exception and task allocations occur only if a timeout actually elapses or
cancellation is triggered; successful acquisitions are otherwise allocation-free. Pooled
IValueTaskSource<TResult> instances are reused to minimize allocation pressure across
repeated lock operations.
private readonly AsyncBarrier _barrier = new AsyncBarrier(3, b =>
{
Console.WriteLine($"Phase {b.CurrentPhase} completed");
});
public async Task ParticipantWorkAsync(CancellationToken ct)
{
// Do some work
await DoPhase1WorkAsync();
// Wait for all participants to complete phase 1
await _barrier.SignalAndWaitAsync(ct);
// All participants continue together to phase 2
await DoPhase2WorkAsync();
}
Constructors
AsyncBarrier(int, Action<AsyncBarrier>?, bool, IGetPooledManualResetValueTaskSource<bool>?)
Constructs a new AsyncBarrier instance with the specified number of participants and a post-phase action.
public AsyncBarrier(int participantCount, Action<AsyncBarrier>? postPhaseAction, bool runContinuationAsynchronously = true, IGetPooledManualResetValueTaskSource<bool>? pool = null)
Parameters
participantCountintThe number of participants required to release the barrier.
postPhaseActionAction<AsyncBarrier>An action to execute after each phase when all participants have arrived. If this action throws an exception, it is wrapped in a BarrierPostPhaseException and thrown to all participants.
runContinuationAsynchronouslyboolIndicates if continuations are forced to run asynchronously.
poolIGetPooledManualResetValueTaskSource<bool>Custom pool for this instance.
Exceptions
- ArgumentOutOfRangeException
Thrown when
participantCountis less than or equal to zero.
AsyncBarrier(int, bool, IGetPooledManualResetValueTaskSource<bool>?)
Constructs a new AsyncBarrier instance with the specified number of participants.
public AsyncBarrier(int participantCount, bool runContinuationAsynchronously = true, IGetPooledManualResetValueTaskSource<bool>? pool = null)
Parameters
participantCountintThe number of participants required to release the barrier.
runContinuationAsynchronouslyboolIndicates if continuations are forced to run asynchronously.
poolIGetPooledManualResetValueTaskSource<bool>Custom pool for this instance.
Exceptions
- ArgumentOutOfRangeException
Thrown when
participantCountis less than or equal to zero.
Properties
CurrentPhase
Gets the current phase number. Increments each time the barrier is released.
public long CurrentPhase { get; }
Property Value
ParticipantCount
Gets the total number of participants in the barrier.
public int ParticipantCount { get; }
Property Value
Remarks
This value changes when AddParticipant(), AddParticipants(int), RemoveParticipant(), or RemoveParticipants(int) is called.
ParticipantsRemaining
Gets the number of participants in the barrier that haven't yet signaled in the current phase.
public int ParticipantsRemaining { get; }
Property Value
RunContinuationAsynchronously
Gets or sets whether to force continuations to run asynchronously.
public bool RunContinuationAsynchronously { get; set; }
Property Value
Methods
AddParticipant()
Notifies the AsyncBarrier that there will be an additional participant.
public long AddParticipant()
Returns
- long
The phase number of the barrier when the participant is added.
Exceptions
- InvalidOperationException
Thrown when adding a participant would cause an overflow.
AddParticipants(int)
Notifies the AsyncBarrier that there will be additional participants.
public long AddParticipants(int participantCount)
Parameters
participantCountintThe number of additional participants to add.
Returns
- long
The phase number of the barrier when the participants are added.
Exceptions
- ArgumentOutOfRangeException
Thrown when
participantCountis less than 1.- InvalidOperationException
Thrown when adding participants would cause an overflow.
RemoveParticipant()
Notifies the AsyncBarrier that there will be one less participant.
public void RemoveParticipant()
Exceptions
- InvalidOperationException
Thrown when there are no participants to remove, or when the barrier would have zero participants.
RemoveParticipants(int)
Notifies the AsyncBarrier that there will be fewer participants.
public void RemoveParticipants(int participantCount)
Parameters
participantCountintThe number of participants to remove.
Exceptions
- ArgumentOutOfRangeException
Thrown when
participantCountis less than 1.- InvalidOperationException
Thrown when there are not enough participants to remove, or when the barrier would have zero participants.
SignalAndWaitAsync(CancellationToken)
Signals the barrier and waits for all participants to arrive.
public ValueTask SignalAndWaitAsync(CancellationToken cancellationToken = default)
Parameters
cancellationTokenCancellationTokenThe cancellation token used to cancel the wait.
Returns
Remarks
When the last participant signals, the post-phase action (if any) is executed, then all waiting participants are released and the barrier resets for the next phase.
Exceptions
- InvalidOperationException
Thrown when more participants signal than expected.
- BarrierPostPhaseException
Thrown when the post-phase action throws an exception.
SignalAndWaitAsync(TimeSpan, CancellationToken)
Signals the barrier and waits for all participants to arrive, or until the specified timeout elapses.
public ValueTask SignalAndWaitAsync(TimeSpan timeout, CancellationToken cancellationToken = default)
Parameters
timeoutTimeSpanThe maximum time to wait for all participants. Use InfiniteTimeSpan to wait indefinitely.
cancellationTokenCancellationTokenThe cancellation token used to cancel the wait.
Returns
Remarks
If this is the last participant, the barrier is released immediately without any timeout overhead. A CancellationTokenSource is allocated only when this is not the last participant and a finite positive timeout is requested; it is disposed automatically when the returned ValueTask is awaited.
Exceptions
- ArgumentOutOfRangeException
Thrown when
timeoutis negative and not equal to InfiniteTimeSpan.- TimeoutException
Thrown when the timeout elapses before all participants arrive.
- OperationCanceledException
Thrown when
cancellationTokenis cancelled before all participants arrive.- InvalidOperationException
Thrown when more participants signal than expected.
- BarrierPostPhaseException
Thrown when the post-phase action throws an exception.