Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
BulkheadPolicy.cs
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
6{
10 sealed class BulkheadPolicy : IAsyncPolicy, IDisposable
11 {
12 private readonly SemaphoreSlim gate;
13 private readonly int maxQueue;
14 private readonly Action? onRejected;
15 private int queued;
16 private bool disposed;
17
18 public BulkheadPolicy(int maxParallel, int maxQueue = 0, Action? onRejected = null)
19 {
20 ArgumentOutOfRangeException.ThrowIfLessThan(maxParallel, 1);
21 ArgumentOutOfRangeException.ThrowIfLessThan(maxQueue, 0);
22
23 this.gate = new SemaphoreSlim(maxParallel, maxParallel);
24 this.maxQueue = maxQueue;
25 this.onRejected = onRejected;
26 }
27
28 public async Task ExecuteAsync(Func<CancellationToken, Task> action, CancellationToken ct)
29 {
30 ObjectDisposedException.ThrowIf(this.disposed, nameof(BulkheadPolicy));
31 bool UsedQueueSlot = false;
32
33 // Quick-path: If no slots and queue is disabled, reject immediately.
34 if (this.gate.CurrentCount == 0 && this.maxQueue == 0)
35 {
36 this.onRejected?.Invoke();
37 throw new InvalidOperationException("Bulkhead is full");
38 }
39
40 // Approximate queue bounding
41 if (this.gate.CurrentCount == 0 && this.maxQueue > 0)
42 {
43 int q = Interlocked.Increment(ref this.queued);
44 if (q > this.maxQueue)
45 {
46 Interlocked.Decrement(ref this.queued);
47 this.onRejected?.Invoke();
48 throw new InvalidOperationException("Bulkhead queue is full");
49 }
50 UsedQueueSlot = true;
51 }
52
53 await this.gate.WaitAsync(ct).ConfigureAwait(false);
54 if (UsedQueueSlot)
55 Interlocked.Decrement(ref this.queued);
56
57 try
58 {
59 await action(ct).ConfigureAwait(false);
60 }
61 finally
62 {
63 this.gate.Release();
64 }
65 }
66
67 public async Task<T> ExecuteAsync<T>(Func<CancellationToken, Task<T>> action, CancellationToken ct)
68 {
69 ObjectDisposedException.ThrowIf(this.disposed, nameof(BulkheadPolicy));
70 bool UsedQueueSlot = false;
71
72 if (this.gate.CurrentCount == 0 && this.maxQueue == 0)
73 {
74 this.onRejected?.Invoke();
75 throw new InvalidOperationException("Bulkhead is full");
76 }
77
78 if (this.gate.CurrentCount == 0 && this.maxQueue > 0)
79 {
80 int q = Interlocked.Increment(ref this.queued);
81 if (q > this.maxQueue)
82 {
83 Interlocked.Decrement(ref this.queued);
84 this.onRejected?.Invoke();
85 throw new InvalidOperationException("Bulkhead queue is full");
86 }
87 UsedQueueSlot = true;
88 }
89
90 await this.gate.WaitAsync(ct).ConfigureAwait(false);
91 if (UsedQueueSlot)
92 Interlocked.Decrement(ref this.queued);
93
94 try
95 {
96 return await action(ct).ConfigureAwait(false);
97 }
98 finally
99 {
100 this.gate.Release();
101 }
102 }
103
104 public void Dispose()
105 {
106 if (this.disposed)
107 return;
108 this.disposed = true;
109 this.gate.Dispose();
110 }
111 }
112}
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.