From e1bc0d955fc639b385b30b070d219c805650f5e6 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 15 May 2026 16:28:43 +0800 Subject: [PATCH 1/7] remove unnecessary RateLimiterHelper --- .../src/System.Threading.RateLimiting.csproj | 1 - .../RateLimiting/ConcurrencyLimiter.cs | 2 +- .../RateLimiting/FixedWindowRateLimiter.cs | 8 +++--- .../RateLimiting/RateLimiterHelper.cs | 25 ------------------- .../RateLimiting/SlidingWindowRateLimiter.cs | 4 +-- .../RateLimiting/TokenBucketRateLimiter.cs | 4 +-- 6 files changed, 9 insertions(+), 35 deletions(-) delete mode 100644 src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs diff --git a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj index 3c88d1d10983f4..072eb957316187 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj +++ b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj @@ -30,7 +30,6 @@ System.Threading.RateLimiting.RateLimitLease - diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs index a4030f8985e8b8..2750990a1c6f6e 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs @@ -32,7 +32,7 @@ public sealed class ConcurrencyLimiter : RateLimiter private object Lock => _queue; /// - public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); /// /// Initializes the . /// diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index ce241a6a342a07..5d7162ec407cda 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -24,10 +24,10 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter /// /// Function to calculate elapsed time from a given tick value. - /// Defaults to . + /// Defaults to . /// In tests, this field can be reassigned via reflection to inject custom time behavior without modifying the public API. /// - private readonly Func _getElapsedTime = RateLimiterHelper.GetElapsedTime; + private readonly Func _getElapsedTime = Stopwatch.GetElapsedTime; private readonly Timer? _renewTimer; private readonly FixedWindowRateLimiterOptions _options; @@ -39,7 +39,7 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter private static readonly RateLimitLease FailedLease = new FixedWindowLease(false, null); /// - public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); /// public override bool IsAutoReplenishing => _options.AutoReplenishment; @@ -301,7 +301,7 @@ private void ReplenishInternal(long nowTicks) return; } - if (RateLimiterHelper.GetElapsedTime(_lastReplenishmentTick, nowTicks) < _options.Window && !_options.AutoReplenishment) + if (Stopwatch.GetElapsedTime(_lastReplenishmentTick, nowTicks) < _options.Window && !_options.AutoReplenishment) { return; } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs deleted file mode 100644 index e538fc720072dd..00000000000000 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Threading.RateLimiting -{ - internal static class RateLimiterHelper - { - public static TimeSpan? GetElapsedTime(long? startTimestamp) - { - if (startTimestamp is null) - { - return null; - } - - return Stopwatch.GetElapsedTime(startTimestamp.Value); - } - - public static TimeSpan GetElapsedTime(long startTimestamp, long endTimestamp) - { - return Stopwatch.GetElapsedTime(startTimestamp, endTimestamp); - } - } -} diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs index 29b16440cd8e03..97db943867915b 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs @@ -36,7 +36,7 @@ public sealed class SlidingWindowRateLimiter : ReplenishingRateLimiter private static readonly RateLimitLease FailedLease = new SlidingWindowLease(false, null); /// - public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); /// public override bool IsAutoReplenishing => _options.AutoReplenishment; @@ -293,7 +293,7 @@ private void ReplenishInternal(long nowTicks) return; } - if (RateLimiterHelper.GetElapsedTime(_lastReplenishmentTick, nowTicks) < ReplenishmentPeriod && !_options.AutoReplenishment) + if (Stopwatch.GetElapsedTime(_lastReplenishmentTick, nowTicks) < ReplenishmentPeriod && !_options.AutoReplenishment) { return; } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs index 5c73c3671eaeff..a0bfc3368a1fff 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs @@ -34,7 +34,7 @@ public sealed class TokenBucketRateLimiter : ReplenishingRateLimiter private static readonly RateLimitLease FailedLease = new TokenBucketLease(false, null); /// - public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); /// public override bool IsAutoReplenishing => _options.AutoReplenishment; @@ -308,7 +308,7 @@ private void ReplenishInternal(long nowTicks) } else { - add = _fillRate * RateLimiterHelper.GetElapsedTime(_lastReplenishmentTick, nowTicks).Ticks; + add = _fillRate * Stopwatch.GetElapsedTime(_lastReplenishmentTick, nowTicks).Ticks; } _tokenCount = Math.Min(_options.TokenLimit, _tokenCount + add); From a0661b6334840c3553aa48eca66449322ee5e522 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 15 May 2026 16:47:05 +0800 Subject: [PATCH 2/7] add extension for nullable support --- .../src/System.Threading.RateLimiting.csproj | 1 + .../RateLimiting/RateLimiterHelper.cs | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs diff --git a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj index 072eb957316187..3c88d1d10983f4 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj +++ b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj @@ -30,6 +30,7 @@ System.Threading.RateLimiting.RateLimitLease + diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs new file mode 100644 index 00000000000000..f0e2e62f9172f5 --- /dev/null +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Threading.RateLimiting +{ + internal static class RateLimiterHelper + { + extension(Stopwatch) + { + public static TimeSpan? GetElapsedTime(long? startTimestamp) + { + if (startTimestamp is null) + { + return null; + } + + return Stopwatch.GetElapsedTime(startTimestamp.Value); + } + } + } +} \ No newline at end of file From 1feec42733d6ed56d3867f93153a65caa038752a Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 16 May 2026 08:46:14 +0800 Subject: [PATCH 3/7] fix build --- .../src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs | 2 +- .../src/System/Threading/RateLimiting/RateLimiterHelper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index 5d7162ec407cda..a1c7b478ab259f 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -24,7 +24,7 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter /// /// Function to calculate elapsed time from a given tick value. - /// Defaults to . + /// Defaults to when has value. /// In tests, this field can be reassigned via reflection to inject custom time behavior without modifying the public API. /// private readonly Func _getElapsedTime = Stopwatch.GetElapsedTime; diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs index f0e2e62f9172f5..2ff6202b3dc950 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs @@ -20,4 +20,4 @@ internal static class RateLimiterHelper } } } -} \ No newline at end of file +} From f2a7a7efc5f62b004d9c44ab88289a3f89a41b35 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 16 May 2026 12:26:16 +0800 Subject: [PATCH 4/7] update comment to remove code reference for Stopwatch.GetElapsedTime --- .../src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index a1c7b478ab259f..c4fa3898e4d280 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -24,7 +24,7 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter /// /// Function to calculate elapsed time from a given tick value. - /// Defaults to when has value. + /// Defaults to Stopwatch.GetElapsedTime(long) when has value. /// In tests, this field can be reassigned via reflection to inject custom time behavior without modifying the public API. /// private readonly Func _getElapsedTime = Stopwatch.GetElapsedTime; From d7ef08e280c6c8bac422476fc1e3f43e93529cec Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 16 May 2026 22:31:22 +0800 Subject: [PATCH 5/7] improve comments Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index c4fa3898e4d280..2af3c948ba0e64 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -24,7 +24,7 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter /// /// Function to calculate elapsed time from a given tick value. - /// Defaults to Stopwatch.GetElapsedTime(long) when has value. + /// Defaults to the Stopwatch.GetElapsedTime(long?) extension which returns when the timestamp is . /// In tests, this field can be reassigned via reflection to inject custom time behavior without modifying the public API. /// private readonly Func _getElapsedTime = Stopwatch.GetElapsedTime; From b9cf51106d5a4515789e35596af7b88b933cda2b Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 16 May 2026 22:46:24 +0800 Subject: [PATCH 6/7] not use the extension to fix copilot comments --- .../Threading/RateLimiting/ConcurrencyLimiter.cs | 2 +- .../RateLimiting/FixedWindowRateLimiter.cs | 4 ++-- .../Threading/RateLimiting/RateLimiterHelper.cs | 13 +++++-------- .../RateLimiting/SlidingWindowRateLimiter.cs | 2 +- .../RateLimiting/TokenBucketRateLimiter.cs | 2 +- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs index 2750990a1c6f6e..a4030f8985e8b8 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs @@ -32,7 +32,7 @@ public sealed class ConcurrencyLimiter : RateLimiter private object Lock => _queue; /// - public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); /// /// Initializes the . /// diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index 2af3c948ba0e64..19aab999fb8371 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -24,10 +24,10 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter /// /// Function to calculate elapsed time from a given tick value. - /// Defaults to the Stopwatch.GetElapsedTime(long?) extension which returns when the timestamp is . + /// Defaults to the which returns when the timestamp is , and returns Stopwatch.GetElapsedTime(long) when not null. /// In tests, this field can be reassigned via reflection to inject custom time behavior without modifying the public API. /// - private readonly Func _getElapsedTime = Stopwatch.GetElapsedTime; + private readonly Func _getElapsedTime = RateLimiterHelper.GetElapsedTime; private readonly Timer? _renewTimer; private readonly FixedWindowRateLimiterOptions _options; diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs index 2ff6202b3dc950..97184ba515082a 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.cs @@ -7,17 +7,14 @@ namespace System.Threading.RateLimiting { internal static class RateLimiterHelper { - extension(Stopwatch) + public static TimeSpan? GetElapsedTime(long? startTimestamp) { - public static TimeSpan? GetElapsedTime(long? startTimestamp) + if (startTimestamp is null) { - if (startTimestamp is null) - { - return null; - } - - return Stopwatch.GetElapsedTime(startTimestamp.Value); + return null; } + + return Stopwatch.GetElapsedTime(startTimestamp.Value); } } } diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs index 97db943867915b..ba2855cc888832 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs @@ -36,7 +36,7 @@ public sealed class SlidingWindowRateLimiter : ReplenishingRateLimiter private static readonly RateLimitLease FailedLease = new SlidingWindowLease(false, null); /// - public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); /// public override bool IsAutoReplenishing => _options.AutoReplenishment; diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs index a0bfc3368a1fff..dfb8ba7316ef6f 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiter.cs @@ -34,7 +34,7 @@ public sealed class TokenBucketRateLimiter : ReplenishingRateLimiter private static readonly RateLimitLease FailedLease = new TokenBucketLease(false, null); /// - public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); /// public override bool IsAutoReplenishing => _options.AutoReplenishment; From a2562fda4afbed5b49b5c129dcdafc2d1744af5f Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 16 May 2026 22:52:17 +0800 Subject: [PATCH 7/7] revert one more RateLimiterHelper.GetElapsedTime --- .../src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs index 19aab999fb8371..09fd69e770f65d 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs +++ b/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs @@ -39,7 +39,7 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter private static readonly RateLimitLease FailedLease = new FixedWindowLease(false, null); /// - public override TimeSpan? IdleDuration => Stopwatch.GetElapsedTime(_idleSince); + public override TimeSpan? IdleDuration => RateLimiterHelper.GetElapsedTime(_idleSince); /// public override bool IsAutoReplenishing => _options.AutoReplenishment;