diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index cad0c8cc8ed..e701998082e 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -4,7 +4,8 @@
-
+
+
diff --git a/src/Polly.Core.Tests/Hedging/HedgingTimeProvider.cs b/src/Polly.Core.Tests/Hedging/HedgingTimeProvider.cs
index f2bddba91ff..d6738df3075 100644
--- a/src/Polly.Core.Tests/Hedging/HedgingTimeProvider.cs
+++ b/src/Polly.Core.Tests/Hedging/HedgingTimeProvider.cs
@@ -1,6 +1,5 @@
using System;
using System.Threading.Tasks;
-using Polly.Utils;
namespace Polly.Core.Tests.Hedging;
@@ -25,15 +24,16 @@ public void Advance(TimeSpan diff)
public List DelayEntries { get; } = new List();
- public override DateTimeOffset UtcNow => _utcNow;
+ public override DateTimeOffset GetUtcNow() => _utcNow;
- public override void CancelAfter(CancellationTokenSource source, TimeSpan delay)
+ public override ITimer CreateTimer(TimerCallback callback, object? state, TimeSpan dueTime, TimeSpan period)
{
- throw new NotSupportedException();
+ return base.CreateTimer(callback, state, dueTime, period);
}
public override Task Delay(TimeSpan delayValue, CancellationToken cancellationToken = default)
{
+
var entry = new DelayEntry(delayValue, new TaskCompletionSource(), _utcNow.Add(delayValue));
cancellationToken.Register(() => entry.Source.TrySetCanceled(cancellationToken));
DelayEntries.Add(entry);
diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs
index ab166eec747..5725ac5ee02 100644
--- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs
+++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs
@@ -272,7 +272,7 @@ private void CloseCircuit_NeedsLock(Outcome outcome, bool manu
private bool PermitHalfOpenCircuitTest_NeedsLock()
{
- var now = _timeProvider.UtcNow;
+ var now = _timeProvider.GetUtcNow();
if (now >= _blockedUntil)
{
_blockedUntil = now + _breakDuration;
@@ -307,7 +307,7 @@ private void OpenCircuit_NeedsLock(Outcome outcome, bool manua
private void OpenCircuitFor_NeedsLock(Outcome outcome, TimeSpan breakDuration, bool manual, ResilienceContext context, out Task? scheduledTask)
{
scheduledTask = null;
- var utcNow = _timeProvider.UtcNow;
+ var utcNow = _timeProvider.GetUtcNow();
_blockedUntil = IsDateTimeOverflow(utcNow, breakDuration) ? DateTimeOffset.MaxValue : utcNow + breakDuration;
diff --git a/src/Polly.Core/CircuitBreaker/Health/RollingHealthMetrics.cs b/src/Polly.Core/CircuitBreaker/Health/RollingHealthMetrics.cs
index 658aea7b7b8..f00806a858a 100644
--- a/src/Polly.Core/CircuitBreaker/Health/RollingHealthMetrics.cs
+++ b/src/Polly.Core/CircuitBreaker/Health/RollingHealthMetrics.cs
@@ -44,7 +44,7 @@ public override HealthInfo GetHealthInfo()
private HealthWindow UpdateCurrentWindow()
{
- var now = TimeProvider.UtcNow;
+ var now = TimeProvider.GetUtcNow();
if (_currentWindow == null || now - _currentWindow.StartedAt >= _windowDuration)
{
_currentWindow = new()
diff --git a/src/Polly.Core/CircuitBreaker/Health/SingleHealthMetrics.cs b/src/Polly.Core/CircuitBreaker/Health/SingleHealthMetrics.cs
index 8afdd1470cc..41748f93229 100644
--- a/src/Polly.Core/CircuitBreaker/Health/SingleHealthMetrics.cs
+++ b/src/Polly.Core/CircuitBreaker/Health/SingleHealthMetrics.cs
@@ -15,7 +15,7 @@ public SingleHealthMetrics(TimeSpan samplingDuration, TimeProvider timeProvider)
: base(timeProvider)
{
_samplingDuration = samplingDuration;
- _startedAt = timeProvider.UtcNow;
+ _startedAt = timeProvider.GetUtcNow();
}
public override void IncrementSuccess()
@@ -32,7 +32,7 @@ public override void IncrementFailure()
public override void Reset()
{
- _startedAt = TimeProvider.UtcNow;
+ _startedAt = TimeProvider.GetUtcNow();
_successes = 0;
_failures = 0;
}
@@ -46,7 +46,7 @@ public override HealthInfo GetHealthInfo()
private void TryReset()
{
- if (TimeProvider.UtcNow - _startedAt >= _samplingDuration)
+ if (TimeProvider.GetUtcNow() - _startedAt >= _samplingDuration)
{
Reset();
}
diff --git a/src/Polly.Core/Hedging/Controller/HedgingController.cs b/src/Polly.Core/Hedging/Controller/HedgingController.cs
index 1cab2b0cfe8..a33ea24da0a 100644
--- a/src/Polly.Core/Hedging/Controller/HedgingController.cs
+++ b/src/Polly.Core/Hedging/Controller/HedgingController.cs
@@ -15,7 +15,7 @@ public HedgingController(TimeProvider provider, HedgingHandler.Handler handler,
_executionPool = new ObjectPool(() =>
{
Interlocked.Increment(ref _rentedExecutions);
- return new TaskExecution(handler);
+ return new TaskExecution(handler, provider);
},
_ =>
{
diff --git a/src/Polly.Core/Hedging/Controller/TaskExecution.cs b/src/Polly.Core/Hedging/Controller/TaskExecution.cs
index 41e5be624d7..c8b9d94a0c5 100644
--- a/src/Polly.Core/Hedging/Controller/TaskExecution.cs
+++ b/src/Polly.Core/Hedging/Controller/TaskExecution.cs
@@ -23,11 +23,16 @@ internal sealed class TaskExecution
{
private readonly ResilienceContext _cachedContext = ResilienceContext.Get();
private readonly HedgingHandler.Handler _handler;
+ private readonly TimeProvider _timeProvider;
private CancellationTokenSource? _cancellationSource;
private CancellationTokenRegistration? _cancellationRegistration;
private ResilienceContext? _activeContext;
- public TaskExecution(HedgingHandler.Handler handler) => _handler = handler;
+ public TaskExecution(HedgingHandler.Handler handler, TimeProvider timeProvider)
+ {
+ _handler = handler;
+ _timeProvider = timeProvider;
+ }
///
/// Gets the task that represents the execution of the hedged task.
@@ -81,7 +86,7 @@ public async ValueTask InitializeAsync(
int attempt)
{
Type = type;
- _cancellationSource = CancellationTokenSourcePool.Get();
+ _cancellationSource = CancellationTokenSourcePool.Get(TimeSpan.Zero, _timeProvider);
Properties.Replace(snapshot.OriginalProperties);
if (snapshot.OriginalCancellationToken.CanBeCanceled)
diff --git a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs
index a4e0d3139d6..92d18a30697 100644
--- a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs
+++ b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs
@@ -5,7 +5,6 @@
using Polly.Hedging;
using Polly.Hedging.Utils;
using Polly.Strategy;
-using Polly.Utils;
namespace Polly;
diff --git a/src/Polly.Core/Polly.Core.csproj b/src/Polly.Core/Polly.Core.csproj
index 87ee858cb7d..ec6199e7813 100644
--- a/src/Polly.Core/Polly.Core.csproj
+++ b/src/Polly.Core/Polly.Core.csproj
@@ -24,6 +24,7 @@
+
diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs
index 8fc889708fd..6b909dfba2a 100644
--- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs
+++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs
@@ -35,8 +35,7 @@ protected internal override async ValueTask ExecuteCoreAsync new CancellationTokenSource(),
static cts => true);
#endif
- public static CancellationTokenSource Get()
+ public static CancellationTokenSource Get(TimeSpan delay, TimeProvider timeProvider)
{
#if NET6_0_OR_GREATER
- return Pool.Get();
+ if (timeProvider != TimeProvider.System)
+ {
+ return timeProvider.CreateCancellationTokenSource(delay);
+ }
+
+ // we are only polling sources when system time provider is used
+ var pooledSource = Pool.Get();
+ if (delay != System.Threading.Timeout.InfiniteTimeSpan)
+ {
+ pooledSource.CancelAfter(delay);
+ }
+
+ return pooledSource;
#else
- return new CancellationTokenSource();
+ return timeProvider.CreateCancellationTokenSource(delay);
#endif
}
diff --git a/src/Polly.Core/Utils/TimeProvider.cs b/src/Polly.Core/Utils/TimeProvider.cs
deleted file mode 100644
index 467c2f21c7e..00000000000
--- a/src/Polly.Core/Utils/TimeProvider.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System.Threading;
-
-namespace Polly.Utils;
-
-#pragma warning disable S3872 // Parameter names should not duplicate the names of their methods
-
-///
-/// TEMPORARY ONLY, to be replaced with System.TimeProvider - https://github.com/dotnet/runtime/issues/36617 later.
-///
-/// We trimmed some of the API that's not relevant for us too.
-internal abstract class TimeProvider
-{
- private readonly double _tickFrequency;
-
- public static TimeProvider System { get; } = new SystemTimeProvider();
-
- protected TimeProvider(long timestampFrequency)
- {
- TimestampFrequency = timestampFrequency;
- _tickFrequency = (double)TimeSpan.TicksPerSecond / TimestampFrequency;
- }
-
- public abstract DateTimeOffset UtcNow { get; }
-
- public long TimestampFrequency { get; }
-
- public abstract long GetTimestamp();
-
- public TimeSpan GetElapsedTime(long startingTimestamp, long endingTimestamp) => new((long)((endingTimestamp - startingTimestamp) * _tickFrequency));
-
- public TimeSpan GetElapsedTime(long startingTimestamp) => GetElapsedTime(startingTimestamp, GetTimestamp());
-
- public abstract Task Delay(TimeSpan delay, CancellationToken cancellationToken = default);
-
- public abstract void CancelAfter(CancellationTokenSource source, TimeSpan delay);
-
- private sealed class SystemTimeProvider : TimeProvider
- {
- public SystemTimeProvider()
- : base(Stopwatch.Frequency)
- {
- }
-
- public override long GetTimestamp() => Stopwatch.GetTimestamp();
-
- public override Task Delay(TimeSpan delay, CancellationToken cancellationToken = default) => Task.Delay(delay, cancellationToken);
-
- public override void CancelAfter(CancellationTokenSource source, TimeSpan delay) => source.CancelAfter(delay);
-
- public override DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
- }
-}