diff --git a/src/Polly.Core.Benchmarks/README.md b/src/Polly.Core.Benchmarks/README.md index 6dfefe05675..705a1a5c615 100644 --- a/src/Polly.Core.Benchmarks/README.md +++ b/src/Polly.Core.Benchmarks/README.md @@ -28,10 +28,10 @@ LaunchCount=2 WarmupCount=10 ## TIMEOUT -| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|------------------ |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:| -| ExecuteTimeout_V7 | 281.5 ns | 5.76 ns | 8.08 ns | 1.00 | 0.00 | 0.0868 | 728 B | 1.00 | -| ExecuteTimeout_V8 | 268.9 ns | 3.86 ns | 5.53 ns | 0.96 | 0.04 | 0.0257 | 216 B | 0.30 | +| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | +|------------------ |---------:|--------:|---------:|------:|--------:|-------:|----------:|------------:| +| ExecuteTimeout_V7 | 304.9 ns | 7.53 ns | 11.27 ns | 1.00 | 0.00 | 0.0868 | 728 B | 1.00 | +| ExecuteTimeout_V8 | 266.5 ns | 5.95 ns | 8.72 ns | 0.88 | 0.04 | - | - | 0.00 | ## RETRY @@ -51,5 +51,5 @@ LaunchCount=2 WarmupCount=10 | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | |--------------------------- |---------:|----------:|----------:|------:|--------:|-------:|----------:|------------:| -| ExecuteStrategyPipeline_V7 | 1.321 us | 0.0355 us | 0.0520 us | 1.00 | 0.00 | 0.2861 | 2400 B | 1.00 | -| ExecuteStrategyPipeline_V8 | 1.126 us | 0.0193 us | 0.0283 us | 0.85 | 0.03 | 0.0763 | 640 B | 0.27 | +| ExecuteStrategyPipeline_V7 | 1.265 us | 0.0372 us | 0.0558 us | 1.00 | 0.00 | 0.2861 | 2400 B | 1.00 | +| ExecuteStrategyPipeline_V8 | 1.032 us | 0.0165 us | 0.0236 us | 0.82 | 0.04 | 0.0076 | 64 B | 0.03 | diff --git a/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs b/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs index 2c183ed7f12..616c5ff653f 100644 --- a/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs +++ b/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs @@ -71,7 +71,7 @@ public void AddTimeout_InvalidOptions_Throws() .Throw().WithMessage("The timeout strategy options are invalid.*"); } - private static TimeSpan GetTimeout(TimeoutResilienceStrategy strategy) => strategy.GetTimeoutAsync(ResilienceContext.Get()).GetAwaiter().GetResult(); + private static TimeSpan GetTimeout(TimeoutResilienceStrategy strategy) => strategy.GetTimeoutAsync(ResilienceContext.Get()).Preserve().GetAwaiter().GetResult(); private static void OnTimeout(TimeoutResilienceStrategy strategy) { diff --git a/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs b/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs index 20c0e93734e..a01570b3912 100644 --- a/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs +++ b/src/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs @@ -63,7 +63,7 @@ public async Task Execute_EnsureOnTimeoutCalled() args.Exception.Should().BeAssignableTo(); args.Timeout.Should().Be(_delay); args.Context.Should().NotBeNull(); - args.Context.CancellationToken.IsCancellationRequested.Should().BeTrue(); + args.Context.CancellationToken.IsCancellationRequested.Should().BeFalse(); called = true; }); diff --git a/src/Polly.Core.Tests/Utils/CancellationTokenSourcePoolTests.cs b/src/Polly.Core.Tests/Utils/CancellationTokenSourcePoolTests.cs new file mode 100644 index 00000000000..b6b163ff9d3 --- /dev/null +++ b/src/Polly.Core.Tests/Utils/CancellationTokenSourcePoolTests.cs @@ -0,0 +1,33 @@ +using Polly.Utils; + +namespace Polly.Core.Tests.Utils; + +public class CancellationTokenSourcePoolTests +{ + [Fact] + public void RentReturn_Reusable_EnsureProperBehavior() + { + var cts = CancellationTokenSourcePool.Get(); + CancellationTokenSourcePool.Return(cts); + + var cts2 = CancellationTokenSourcePool.Get(); +#if NET6_0_OR_GREATER + cts2.Should().BeSameAs(cts); +#else + cts2.Should().NotBeSameAs(cts); +#endif + } + + [Fact] + public void RentReturn_NotReusable_EnsureProperBehavior() + { + var cts = CancellationTokenSourcePool.Get(); + cts.Cancel(); + CancellationTokenSourcePool.Return(cts); + + cts.Invoking(c => c.Token).Should().Throw(); + + var cts2 = CancellationTokenSourcePool.Get(); + cts2.Token.Should().NotBeNull(); + } +} diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index 7a9c492bed0..8e1b7e68110 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -35,7 +35,7 @@ protected internal override async ValueTask ExecuteCoreAsync ExecuteCoreAsync ExecuteCoreAsync GetTimeoutAsync(ResilienceContext context) + internal async ValueTask GetTimeoutAsync(ResilienceContext context) { if (TimeoutGenerator == null) { diff --git a/src/Polly.Core/Utils/CancellationTokenSourcePool.cs b/src/Polly.Core/Utils/CancellationTokenSourcePool.cs new file mode 100644 index 00000000000..5e0d1d4fd18 --- /dev/null +++ b/src/Polly.Core/Utils/CancellationTokenSourcePool.cs @@ -0,0 +1,31 @@ +namespace Polly.Utils +{ + internal static class CancellationTokenSourcePool + { +#if NET6_0_OR_GREATER + private static readonly ObjectPool Pool = new( + static () => new CancellationTokenSource(), + static cts => true); +#endif + public static CancellationTokenSource Get() + { +#if NET6_0_OR_GREATER + return Pool.Get(); +#else + return new CancellationTokenSource(); +#endif + } + + public static void Return(CancellationTokenSource source) + { +#if NET6_0_OR_GREATER + if (source.TryReset()) + { + Pool.Return(source); + return; + } +#endif + source.Dispose(); + } + } +} diff --git a/src/Polly.Core/Utils/TimeProviderExtensions.cs b/src/Polly.Core/Utils/TimeProviderExtensions.cs index dd3b51203d4..a63b842790e 100644 --- a/src/Polly.Core/Utils/TimeProviderExtensions.cs +++ b/src/Polly.Core/Utils/TimeProviderExtensions.cs @@ -25,6 +25,7 @@ public static Task DelayAsync(this TimeProvider timeProvider, TimeSpan delay, Re if (context.IsSynchronous && timeProvider == TimeProvider.System) { + // Stryker disable once boolean : no means to test this if (context.CancellationToken.CanBeCanceled) { context.CancellationToken.WaitHandle.WaitOne(delay);