Skip to content

Commit 0540772

Browse files
Add RateLimiting APIs (#61788)
1 parent 62b2333 commit 0540772

21 files changed

+2710
-1
lines changed

docs/area-owners.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue
128128
| area-System.Text.RegularExpressions | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @stephentoub |
129129
| area-System.Threading | @mangod9 | @kouvel | |
130130
| area-System.Threading.Channels | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @stephentoub |
131+
| area-System.Threading.RateLimiting | @rafikiassumani-msft | @BrennanConroy @halter73 | Consultants: @eerhardt |
131132
| area-System.Threading.Tasks | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @stephentoub |
132133
| area-System.Transactions | @HongGit | @HongGit | |
133134
| area-System.Xml | @jeffhandley | @eiriktsarpalis @krwq @layomia | |

src/libraries/System.Threading.Channels/src/System/Collections/Generic/Deque.cs renamed to src/libraries/Common/src/System/Collections/Generic/Deque.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ public T PeekHead()
6969
return _array[_head];
7070
}
7171

72+
public T PeekTail()
73+
{
74+
Debug.Assert(!IsEmpty); // caller's responsibility to make sure there are elements remaining
75+
var index = _tail - 1;
76+
if (index == -1)
77+
{
78+
index = _array.Length - 1;
79+
}
80+
return _array[index];
81+
}
82+
7283
public T DequeueTail()
7384
{
7485
Debug.Assert(!IsEmpty); // caller's responsibility to make sure there are elements remaining

src/libraries/System.Threading.Channels/src/System.Threading.Channels.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ System.Threading.Channel&lt;T&gt;</PackageDescription>
1111
</PropertyGroup>
1212
<ItemGroup>
1313
<Compile Include="System\VoidResult.cs" />
14-
<Compile Include="System\Collections\Generic\Deque.cs" />
1514
<Compile Include="System\Threading\Channels\AsyncOperation.cs" />
1615
<Compile Include="System\Threading\Channels\AsyncOperation.netcoreapp.cs"
1716
Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
@@ -40,6 +39,8 @@ System.Threading.Channel&lt;T&gt;</PackageDescription>
4039
Link="Common\Internal\Padding.cs" />
4140
<Compile Include="$(CommonPath)System\Collections\Concurrent\SingleProducerConsumerQueue.cs"
4241
Link="Common\System\Collections\Concurrent\SingleProducerConsumerQueue.cs" />
42+
<Compile Include="$(CommonPath)System\Collections\Generic\Deque.cs"
43+
Link="Common\System\Collections\Generic\Deque.cs" />
4344
</ItemGroup>
4445
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
4546
<Reference Include="System.Collections" />
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Microsoft Visual Studio Solution File, Format Version 12.00
2+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{CAEE0409-CCC3-4EA6-AB54-177FD305D42D}"
3+
EndProject
4+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj", "{39DA5B84-ECA2-42A2-BEBD-C056BDB8AD53}"
5+
EndProject
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\src\Microsoft.Bcl.AsyncInterfaces.csproj", "{F59F4FD7-EA00-47EA-A09A-6F76CB079F9B}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj", "{0D1C7DCB-970D-4099-AC9F-A01E75923EC6}"
9+
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\src\System.Runtime.CompilerServices.Unsafe.ilproj", "{AF838F1D-5C1C-472B-B31C-9A3B7507BB4B}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.DllImportGenerator", "..\System.Runtime.InteropServices\gen\DllImportGenerator\DllImportGenerator.csproj", "{1E52F495-578C-4FDB-86DD-87EAAE0A0BE7}"
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{25495BDC-0614-4FAC-B6EA-DF3F0E35A871}"
15+
EndProject
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.RateLimiting", "ref\System.Threading.RateLimiting.csproj", "{FD274A80-0D68-48A0-9AC7-279C9E69BC63}"
17+
EndProject
18+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.RateLimiting", "src\System.Threading.RateLimiting.csproj", "{CD96AFE9-0F7F-42FA-BBDA-F57EDCBB4609}"
19+
EndProject
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.RateLimiting.Tests", "tests\System.Threading.RateLimiting.Tests.csproj", "{AE81EE9F-1240-4AF1-BF21-7F451B7859E5}"
21+
EndProject
22+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{6614EF7F-23FC-4809-AFF5-1ADBF1B6422C}"
23+
EndProject
24+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{111B1B5B-A004-4C05-9A8C-E0931DADA5FB}"
25+
EndProject
26+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{85204CF5-0C88-4BBB-9E70-D8CCED82ED3D}"
27+
EndProject
28+
Global
29+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
30+
Debug|Any CPU = Debug|Any CPU
31+
Release|Any CPU = Release|Any CPU
32+
EndGlobalSection
33+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
34+
{CAEE0409-CCC3-4EA6-AB54-177FD305D42D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{CAEE0409-CCC3-4EA6-AB54-177FD305D42D}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{CAEE0409-CCC3-4EA6-AB54-177FD305D42D}.Release|Any CPU.ActiveCfg = Release|Any CPU
37+
{CAEE0409-CCC3-4EA6-AB54-177FD305D42D}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{39DA5B84-ECA2-42A2-BEBD-C056BDB8AD53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{39DA5B84-ECA2-42A2-BEBD-C056BDB8AD53}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{39DA5B84-ECA2-42A2-BEBD-C056BDB8AD53}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{39DA5B84-ECA2-42A2-BEBD-C056BDB8AD53}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{F59F4FD7-EA00-47EA-A09A-6F76CB079F9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{F59F4FD7-EA00-47EA-A09A-6F76CB079F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{F59F4FD7-EA00-47EA-A09A-6F76CB079F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{F59F4FD7-EA00-47EA-A09A-6F76CB079F9B}.Release|Any CPU.Build.0 = Release|Any CPU
46+
{0D1C7DCB-970D-4099-AC9F-A01E75923EC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{0D1C7DCB-970D-4099-AC9F-A01E75923EC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{0D1C7DCB-970D-4099-AC9F-A01E75923EC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{0D1C7DCB-970D-4099-AC9F-A01E75923EC6}.Release|Any CPU.Build.0 = Release|Any CPU
50+
{AF838F1D-5C1C-472B-B31C-9A3B7507BB4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51+
{AF838F1D-5C1C-472B-B31C-9A3B7507BB4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
52+
{AF838F1D-5C1C-472B-B31C-9A3B7507BB4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{AF838F1D-5C1C-472B-B31C-9A3B7507BB4B}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{1E52F495-578C-4FDB-86DD-87EAAE0A0BE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{1E52F495-578C-4FDB-86DD-87EAAE0A0BE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{1E52F495-578C-4FDB-86DD-87EAAE0A0BE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{1E52F495-578C-4FDB-86DD-87EAAE0A0BE7}.Release|Any CPU.Build.0 = Release|Any CPU
58+
{25495BDC-0614-4FAC-B6EA-DF3F0E35A871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59+
{25495BDC-0614-4FAC-B6EA-DF3F0E35A871}.Debug|Any CPU.Build.0 = Debug|Any CPU
60+
{25495BDC-0614-4FAC-B6EA-DF3F0E35A871}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{25495BDC-0614-4FAC-B6EA-DF3F0E35A871}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{FD274A80-0D68-48A0-9AC7-279C9E69BC63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{FD274A80-0D68-48A0-9AC7-279C9E69BC63}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{FD274A80-0D68-48A0-9AC7-279C9E69BC63}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{FD274A80-0D68-48A0-9AC7-279C9E69BC63}.Release|Any CPU.Build.0 = Release|Any CPU
66+
{CD96AFE9-0F7F-42FA-BBDA-F57EDCBB4609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{CD96AFE9-0F7F-42FA-BBDA-F57EDCBB4609}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{CD96AFE9-0F7F-42FA-BBDA-F57EDCBB4609}.Release|Any CPU.ActiveCfg = Release|Any CPU
69+
{CD96AFE9-0F7F-42FA-BBDA-F57EDCBB4609}.Release|Any CPU.Build.0 = Release|Any CPU
70+
{AE81EE9F-1240-4AF1-BF21-7F451B7859E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
71+
{AE81EE9F-1240-4AF1-BF21-7F451B7859E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
72+
{AE81EE9F-1240-4AF1-BF21-7F451B7859E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
73+
{AE81EE9F-1240-4AF1-BF21-7F451B7859E5}.Release|Any CPU.Build.0 = Release|Any CPU
74+
EndGlobalSection
75+
GlobalSection(SolutionProperties) = preSolution
76+
HideSolutionNode = FALSE
77+
EndGlobalSection
78+
GlobalSection(NestedProjects) = preSolution
79+
{CAEE0409-CCC3-4EA6-AB54-177FD305D42D} = {6614EF7F-23FC-4809-AFF5-1ADBF1B6422C}
80+
{AE81EE9F-1240-4AF1-BF21-7F451B7859E5} = {6614EF7F-23FC-4809-AFF5-1ADBF1B6422C}
81+
{39DA5B84-ECA2-42A2-BEBD-C056BDB8AD53} = {111B1B5B-A004-4C05-9A8C-E0931DADA5FB}
82+
{0D1C7DCB-970D-4099-AC9F-A01E75923EC6} = {111B1B5B-A004-4C05-9A8C-E0931DADA5FB}
83+
{FD274A80-0D68-48A0-9AC7-279C9E69BC63} = {111B1B5B-A004-4C05-9A8C-E0931DADA5FB}
84+
{F59F4FD7-EA00-47EA-A09A-6F76CB079F9B} = {85204CF5-0C88-4BBB-9E70-D8CCED82ED3D}
85+
{AF838F1D-5C1C-472B-B31C-9A3B7507BB4B} = {85204CF5-0C88-4BBB-9E70-D8CCED82ED3D}
86+
{1E52F495-578C-4FDB-86DD-87EAAE0A0BE7} = {85204CF5-0C88-4BBB-9E70-D8CCED82ED3D}
87+
{25495BDC-0614-4FAC-B6EA-DF3F0E35A871} = {85204CF5-0C88-4BBB-9E70-D8CCED82ED3D}
88+
{CD96AFE9-0F7F-42FA-BBDA-F57EDCBB4609} = {85204CF5-0C88-4BBB-9E70-D8CCED82ED3D}
89+
EndGlobalSection
90+
GlobalSection(ExtensibilityGlobals) = postSolution
91+
SolutionGuid = {25036AEF-71B3-4C8A-891F-0350414F9A23}
92+
EndGlobalSection
93+
EndGlobal
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// ------------------------------------------------------------------------------
4+
// Changes to this file must follow the https://aka.ms/api-review process.
5+
// ------------------------------------------------------------------------------
6+
7+
namespace System.Threading.RateLimiting
8+
{
9+
public sealed partial class ConcurrencyLimiter : System.Threading.RateLimiting.RateLimiter
10+
{
11+
public ConcurrencyLimiter(System.Threading.RateLimiting.ConcurrencyLimiterOptions options) { }
12+
protected override System.Threading.RateLimiting.RateLimitLease AcquireCore(int permitCount) { throw null; }
13+
protected override void Dispose(bool disposing) { }
14+
protected override System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; }
15+
public override int GetAvailablePermits() { throw null; }
16+
protected override System.Threading.Tasks.ValueTask<System.Threading.RateLimiting.RateLimitLease> WaitAsyncCore(int permitCount, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
17+
}
18+
public sealed partial class ConcurrencyLimiterOptions
19+
{
20+
public ConcurrencyLimiterOptions(int permitLimit, System.Threading.RateLimiting.QueueProcessingOrder queueProcessingOrder, int queueLimit) { }
21+
public int PermitLimit { get { throw null; } }
22+
public int QueueLimit { get { throw null; } }
23+
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } }
24+
}
25+
public static partial class MetadataName
26+
{
27+
public static System.Threading.RateLimiting.MetadataName<string> ReasonPhrase { get { throw null; } }
28+
public static System.Threading.RateLimiting.MetadataName<System.TimeSpan> RetryAfter { get { throw null; } }
29+
public static System.Threading.RateLimiting.MetadataName<T> Create<T>(string name) { throw null; }
30+
}
31+
public sealed partial class MetadataName<T> : System.IEquatable<System.Threading.RateLimiting.MetadataName<T>>
32+
{
33+
public MetadataName(string name) { }
34+
public string Name { get { throw null; } }
35+
public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; }
36+
public bool Equals(System.Threading.RateLimiting.MetadataName<T>? other) { throw null; }
37+
public override int GetHashCode() { throw null; }
38+
public static bool operator ==(System.Threading.RateLimiting.MetadataName<T> left, System.Threading.RateLimiting.MetadataName<T> right) { throw null; }
39+
public static bool operator !=(System.Threading.RateLimiting.MetadataName<T> left, System.Threading.RateLimiting.MetadataName<T> right) { throw null; }
40+
public override string ToString() { throw null; }
41+
}
42+
public enum QueueProcessingOrder
43+
{
44+
OldestFirst = 0,
45+
NewestFirst = 1,
46+
}
47+
public abstract partial class RateLimiter : System.IAsyncDisposable, System.IDisposable
48+
{
49+
protected RateLimiter() { }
50+
public System.Threading.RateLimiting.RateLimitLease Acquire(int permitCount = 1) { throw null; }
51+
protected abstract System.Threading.RateLimiting.RateLimitLease AcquireCore(int permitCount);
52+
public void Dispose() { }
53+
protected virtual void Dispose(bool disposing) { }
54+
public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
55+
protected virtual System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; }
56+
public abstract int GetAvailablePermits();
57+
public System.Threading.Tasks.ValueTask<System.Threading.RateLimiting.RateLimitLease> WaitAsync(int permitCount = 1, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
58+
protected abstract System.Threading.Tasks.ValueTask<System.Threading.RateLimiting.RateLimitLease> WaitAsyncCore(int permitCount, System.Threading.CancellationToken cancellationToken);
59+
}
60+
public abstract partial class RateLimitLease : System.IDisposable
61+
{
62+
protected RateLimitLease() { }
63+
public abstract bool IsAcquired { get; }
64+
public abstract System.Collections.Generic.IEnumerable<string> MetadataNames { get; }
65+
public void Dispose() { }
66+
protected virtual void Dispose(bool disposing) { }
67+
public virtual System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>> GetAllMetadata() { throw null; }
68+
public abstract bool TryGetMetadata(string metadataName, out object? metadata);
69+
public bool TryGetMetadata<T>(System.Threading.RateLimiting.MetadataName<T> metadataName, [System.Diagnostics.CodeAnalysis.MaybeNullAttribute] out T metadata) { throw null; }
70+
}
71+
public sealed partial class TokenBucketRateLimiter : System.Threading.RateLimiting.RateLimiter
72+
{
73+
public TokenBucketRateLimiter(System.Threading.RateLimiting.TokenBucketRateLimiterOptions options) { }
74+
protected override System.Threading.RateLimiting.RateLimitLease AcquireCore(int tokenCount) { throw null; }
75+
protected override void Dispose(bool disposing) { }
76+
protected override System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; }
77+
public override int GetAvailablePermits() { throw null; }
78+
public bool TryReplenish() { throw null; }
79+
protected override System.Threading.Tasks.ValueTask<System.Threading.RateLimiting.RateLimitLease> WaitAsyncCore(int tokenCount, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
80+
}
81+
public sealed partial class TokenBucketRateLimiterOptions
82+
{
83+
public TokenBucketRateLimiterOptions(int tokenLimit, System.Threading.RateLimiting.QueueProcessingOrder queueProcessingOrder, int queueLimit, System.TimeSpan replenishmentPeriod, int tokensPerPeriod, bool autoReplenishment = true) { }
84+
public bool AutoReplenishment { get { throw null; } }
85+
public int QueueLimit { get { throw null; } }
86+
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } }
87+
public System.TimeSpan ReplenishmentPeriod { get { throw null; } }
88+
public int TokenLimit { get { throw null; } }
89+
public int TokensPerPeriod { get { throw null; } }
90+
}
91+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
4+
<Nullable>enable</Nullable>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="System.Threading.RateLimiting.cs" />
8+
</ItemGroup>
9+
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
10+
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\ref\System.Runtime.csproj" />
11+
</ItemGroup>
12+
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and '$(TargetFramework)' != '$(NetCoreAppCurrent)'">
13+
<Reference Include="System.Runtime" />
14+
</ItemGroup>
15+
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
16+
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsVersion)" />
17+
</ItemGroup>
18+
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'netstandard2.1'))">
19+
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj" />
20+
</ItemGroup>
21+
</Project>

0 commit comments

Comments
 (0)