Table of Contents

Class AdaptiveSemaphore

Namespace
MADE.Threading
Assembly
MADE.Threading.dll

Defines a semaphore that allows adjusting the concurrency limit at runtime to respond to backpressure.

public sealed class AdaptiveSemaphore : IDisposable
Inheritance
AdaptiveSemaphore
Implements
Inherited Members
Extension Methods

Remarks

This is useful for scenarios where the permitted concurrency should change dynamically, such as reducing parallelism when a downstream service returns rate-limit (429) responses or increasing it when conditions improve.

var semaphore = new AdaptiveSemaphore(initial: 10, minimum: 1, maximum: 20);

// Normal usage - acquire and release a permit.
using (await semaphore.WaitAsync())
{
    await httpClient.SendAsync(request);
}

// Reduce concurrency on backpressure.
await semaphore.TryShrinkAsync();

Constructors

AdaptiveSemaphore(int, int, int?)

Initializes a new instance of the AdaptiveSemaphore class.

public AdaptiveSemaphore(int initial, int minimum = 1, int? maximum = null)

Parameters

initial int

The initial number of concurrent permits.

minimum int

The minimum concurrency limit. Defaults to 1.

maximum int?

The optional maximum concurrency limit.

Exceptions

ArgumentOutOfRangeException

Thrown when minimum is less than 1, or maximum is less than minimum.

Properties

Available

Gets the number of permits currently available.

public int Available { get; }

Property Value

int

Limit

Gets the current concurrency limit.

public int Limit { get; }

Property Value

int

Methods

Dispose()

Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

public void Dispose()

TryGrow()

Increases the concurrency limit by one by releasing an additional permit.

public int TryGrow()

Returns

int

The new concurrency limit.

Remarks

If the limit is already at the MADE.Threading.AdaptiveSemaphore.maximum, this method does nothing. This is the inverse of TryShrinkAsync(CancellationToken) and should be called when conditions improve and more concurrency is desirable.

Exceptions

ObjectDisposedException

Thrown if the semaphore has been disposed.

TryShrinkAsync(CancellationToken)

Reduces the concurrency limit by one by permanently acquiring a permit.

public Task<int> TryShrinkAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

The cancellation token.

Returns

Task<int>

The new concurrency limit.

Remarks

If the limit is already at the MADE.Threading.AdaptiveSemaphore.minimum, this method does nothing. The acquired permit is not released, effectively reducing the pool of available permits.

Exceptions

ObjectDisposedException

Thrown if the semaphore has been disposed.

Wait(CancellationToken)

Synchronously waits to acquire a permit from the semaphore.

public IDisposable Wait(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

The cancellation token.

Returns

IDisposable

An IDisposable that releases the permit when disposed.

Exceptions

ObjectDisposedException

Thrown if the semaphore has been disposed.

WaitAsync(CancellationToken)

Asynchronously waits to acquire a permit from the semaphore.

public Task<IDisposable> WaitAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

The cancellation token.

Returns

Task<IDisposable>

An IDisposable that releases the permit when disposed.

Exceptions

ObjectDisposedException

Thrown if the semaphore has been disposed.