3using System.Threading.Tasks;
12 private readonly SemaphoreSlim gate;
13 private readonly
int maxQueue;
14 private readonly Action? onRejected;
16 private bool disposed;
18 public BulkheadPolicy(
int maxParallel,
int maxQueue = 0, Action? onRejected =
null)
20 ArgumentOutOfRangeException.ThrowIfLessThan(maxParallel, 1);
21 ArgumentOutOfRangeException.ThrowIfLessThan(maxQueue, 0);
23 this.gate =
new SemaphoreSlim(maxParallel, maxParallel);
24 this.maxQueue = maxQueue;
25 this.onRejected = onRejected;
28 public async Task ExecuteAsync(Func<CancellationToken, Task> action, CancellationToken ct)
30 ObjectDisposedException.ThrowIf(this.disposed, nameof(
BulkheadPolicy));
31 bool UsedQueueSlot =
false;
34 if (this.gate.CurrentCount == 0 &&
this.maxQueue == 0)
36 this.onRejected?.Invoke();
37 throw new InvalidOperationException(
"Bulkhead is full");
41 if (this.gate.CurrentCount == 0 &&
this.maxQueue > 0)
43 int q = Interlocked.Increment(ref this.queued);
44 if (q > this.maxQueue)
46 Interlocked.Decrement(ref this.queued);
47 this.onRejected?.Invoke();
48 throw new InvalidOperationException(
"Bulkhead queue is full");
53 await this.gate.WaitAsync(ct).ConfigureAwait(
false);
55 Interlocked.Decrement(ref this.queued);
59 await action(ct).ConfigureAwait(
false);
67 public async Task<T>
ExecuteAsync<T>(Func<CancellationToken, Task<T>> action, CancellationToken ct)
69 ObjectDisposedException.ThrowIf(this.disposed, nameof(
BulkheadPolicy));
70 bool UsedQueueSlot =
false;
72 if (this.gate.CurrentCount == 0 &&
this.maxQueue == 0)
74 this.onRejected?.Invoke();
75 throw new InvalidOperationException(
"Bulkhead is full");
78 if (this.gate.CurrentCount == 0 &&
this.maxQueue > 0)
80 int q = Interlocked.Increment(ref this.queued);
81 if (q > this.maxQueue)
83 Interlocked.Decrement(ref this.queued);
84 this.onRejected?.Invoke();
85 throw new InvalidOperationException(
"Bulkhead queue is full");
90 await this.gate.WaitAsync(ct).ConfigureAwait(
false);
92 Interlocked.Decrement(ref this.queued);
96 return await action(ct).ConfigureAwait(
false);
104 public void Dispose()
108 this.disposed =
true;
Limits parallel executions, optionally bounding the queue. Rejects when saturated.
async Task< T > ExecuteAsync< T >(Func< CancellationToken, Task< T > > action, CancellationToken ct)
Executes an async action producing a value through the policy pipeline.