Table of Contents

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

participantCount int

The number of participants required to release the barrier.

postPhaseAction Action<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.

runContinuationAsynchronously bool

Indicates if continuations are forced to run asynchronously.

pool IGetPooledManualResetValueTaskSource<bool>

Custom pool for this instance.

Exceptions

ArgumentOutOfRangeException

Thrown when participantCount is 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

participantCount int

The number of participants required to release the barrier.

runContinuationAsynchronously bool

Indicates if continuations are forced to run asynchronously.

pool IGetPooledManualResetValueTaskSource<bool>

Custom pool for this instance.

Exceptions

ArgumentOutOfRangeException

Thrown when participantCount is 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

long

ParticipantCount

Gets the total number of participants in the barrier.

public int ParticipantCount { get; }

Property Value

int

Remarks

ParticipantsRemaining

Gets the number of participants in the barrier that haven't yet signaled in the current phase.

public int ParticipantsRemaining { get; }

Property Value

int

RunContinuationAsynchronously

Gets or sets whether to force continuations to run asynchronously.

public bool RunContinuationAsynchronously { get; set; }

Property Value

bool

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

participantCount int

The number of additional participants to add.

Returns

long

The phase number of the barrier when the participants are added.

Exceptions

ArgumentOutOfRangeException

Thrown when participantCount is 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

participantCount int

The number of participants to remove.

Exceptions

ArgumentOutOfRangeException

Thrown when participantCount is 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

cancellationToken CancellationToken

The cancellation token used to cancel the wait.

Returns

ValueTask

A ValueTask that completes when all participants have arrived.

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

timeout TimeSpan

The maximum time to wait for all participants. Use InfiniteTimeSpan to wait indefinitely.

cancellationToken CancellationToken

The cancellation token used to cancel the wait.

Returns

ValueTask

A ValueTask that completes when all participants have arrived.

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 timeout is negative and not equal to InfiniteTimeSpan.

TimeoutException

Thrown when the timeout elapses before all participants arrive.

OperationCanceledException

Thrown when cancellationToken is cancelled before all participants arrive.

InvalidOperationException

Thrown when more participants signal than expected.

BarrierPostPhaseException

Thrown when the post-phase action throws an exception.