-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RateLimiting APIs #61788
Add RateLimiting APIs #61788
Changes from 1 commit
b0c978b
2877bc1
e469136
f59ff02
bb4d228
152e894
822efc3
dc660d0
4b0709b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ public sealed class ConcurrencyLimiter : RateLimiter | |
{ | ||
private int _permitCount; | ||
private int _queueCount; | ||
private bool _disposed; | ||
|
||
private readonly ConcurrencyLimiterOptions _options; | ||
private readonly Deque<RequestRegistration> _queue = new Deque<RequestRegistration>(); | ||
|
@@ -49,7 +50,7 @@ protected override RateLimitLease AcquireCore(int permitCount) | |
} | ||
|
||
// Return SuccessfulLease or FailedLease to indicate limiter state | ||
if (permitCount == 0) | ||
if (permitCount == 0 && !_disposed) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to throw ObjectDisposedException if this has been disposed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should an ODE throw synchronously or be wrapped in a ValueTask? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess the first question is:
Doing a quick check of other System.Threading disposable types, they seem to throw ODE: Barrier, ManualResetEventSlim, ReaderWriterLockSlim For the next question, I would say it is similar to argument checking - so we would throw inline. |
||
{ | ||
return _permitCount > 0 ? SuccessfulLease : FailedLease; | ||
} | ||
|
@@ -79,7 +80,7 @@ protected override ValueTask<RateLimitLease> WaitAsyncCore(int permitCount, Canc | |
} | ||
|
||
// Return SuccessfulLease if requestedCount is 0 and resources are available | ||
if (permitCount == 0 && _permitCount > 0) | ||
if (permitCount == 0 && _permitCount > 0 && !_disposed) | ||
{ | ||
return new ValueTask<RateLimitLease>(SuccessfulLease); | ||
} | ||
|
@@ -95,7 +96,6 @@ protected override ValueTask<RateLimitLease> WaitAsyncCore(int permitCount, Canc | |
// Don't queue if queue limit reached | ||
if (_queueCount + permitCount > _options.QueueLimit) | ||
{ | ||
// Perf: static failed/successful value tasks? | ||
return new ValueTask<RateLimitLease>(QueueLimitLease); | ||
} | ||
|
||
|
@@ -120,6 +120,12 @@ protected override ValueTask<RateLimitLease> WaitAsyncCore(int permitCount, Canc | |
|
||
private bool TryLeaseUnsynchronized(int permitCount, [NotNullWhen(true)] out RateLimitLease? lease) | ||
{ | ||
if (_disposed) | ||
{ | ||
lease = FailedLease; | ||
return true; | ||
} | ||
|
||
// if permitCount is 0 we want to queue it if there are no available permits | ||
if (_permitCount >= permitCount && _permitCount != 0) | ||
{ | ||
|
@@ -149,6 +155,11 @@ private void Release(int releaseCount) | |
{ | ||
lock (Lock) | ||
{ | ||
if (_disposed) | ||
{ | ||
return; | ||
} | ||
|
||
_permitCount += releaseCount; | ||
Debug.Assert(_permitCount <= _options.PermitLimit); | ||
|
||
|
@@ -188,6 +199,22 @@ private void Release(int releaseCount) | |
} | ||
} | ||
|
||
public override void Dispose() | ||
{ | ||
lock (Lock) | ||
{ | ||
_disposed = true; | ||
while (_queue.Count > 0) | ||
{ | ||
RequestRegistration next = _options.QueueProcessingOrder == QueueProcessingOrder.OldestFirst | ||
? _queue.DequeueHead() | ||
: _queue.DequeueTail(); | ||
next.CancellationTokenRegistration.Dispose(); | ||
next.Tcs.SetResult(FailedLease); | ||
} | ||
} | ||
} | ||
|
||
private sealed class ConcurrencyLease : RateLimitLease | ||
{ | ||
private static readonly string[] s_allMetadataNames = new[] { MetadataName.ReasonPhrase.Name }; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approved API called these:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approved API code was wrong, the comment above the code states: