diff --git a/src/Polly.Core.Benchmarks/README.md b/src/Polly.Core.Benchmarks/README.md index 2c57f130f89..6dfefe05675 100644 --- a/src/Polly.Core.Benchmarks/README.md +++ b/src/Polly.Core.Benchmarks/README.md @@ -12,44 +12,44 @@ LaunchCount=2 WarmupCount=10 ## PIPELINES -| Method | Components | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|------------------- |----------- |------------:|----------:|----------:|------:|--------:|-------:|----------:|------------:| -| ExecutePipeline_V7 | 1 | 71.29 ns | 1.309 ns | 1.959 ns | 1.00 | 0.00 | 0.0362 | 304 B | 1.00 | -| ExecutePipeline_V8 | 1 | 92.93 ns | 1.398 ns | 2.049 ns | 1.30 | 0.05 | 0.0181 | 152 B | 0.50 | -| | | | | | | | | | | -| ExecutePipeline_V7 | 2 | 164.39 ns | 5.294 ns | 7.592 ns | 1.00 | 0.00 | 0.0658 | 552 B | 1.00 | -| ExecutePipeline_V8 | 2 | 126.74 ns | 1.198 ns | 1.755 ns | 0.77 | 0.04 | 0.0181 | 152 B | 0.28 | -| | | | | | | | | | | -| ExecutePipeline_V7 | 5 | 540.07 ns | 16.941 ns | 24.832 ns | 1.00 | 0.00 | 0.1545 | 1296 B | 1.00 | -| ExecutePipeline_V8 | 5 | 257.13 ns | 2.748 ns | 4.114 ns | 0.48 | 0.03 | 0.0181 | 152 B | 0.12 | -| | | | | | | | | | | -| ExecutePipeline_V7 | 10 | 1,111.72 ns | 16.405 ns | 23.527 ns | 1.00 | 0.00 | 0.3014 | 2536 B | 1.00 | -| ExecutePipeline_V8 | 10 | 467.93 ns | 6.546 ns | 9.388 ns | 0.42 | 0.01 | 0.0181 | 152 B | 0.06 | +| Method | Components | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | +|------------------- |----------- |------------:|----------:|-----------:|------------:|------:|--------:|-------:|----------:|------------:| +| ExecutePipeline_V7 | 1 | 74.78 ns | 1.555 ns | 2.279 ns | 75.63 ns | 1.00 | 0.00 | 0.0362 | 304 B | 1.00 | +| ExecutePipeline_V8 | 1 | 85.69 ns | 0.500 ns | 0.732 ns | 85.36 ns | 1.15 | 0.04 | - | - | 0.00 | +| | | | | | | | | | | | +| ExecutePipeline_V7 | 2 | 165.37 ns | 1.157 ns | 1.732 ns | 165.59 ns | 1.00 | 0.00 | 0.0658 | 552 B | 1.00 | +| ExecutePipeline_V8 | 2 | 119.10 ns | 0.653 ns | 0.915 ns | 119.63 ns | 0.72 | 0.01 | - | - | 0.00 | +| | | | | | | | | | | | +| ExecutePipeline_V7 | 5 | 533.97 ns | 7.327 ns | 10.967 ns | 536.79 ns | 1.00 | 0.00 | 0.1545 | 1296 B | 1.00 | +| ExecutePipeline_V8 | 5 | 227.69 ns | 1.236 ns | 1.812 ns | 227.72 ns | 0.43 | 0.01 | - | - | 0.00 | +| | | | | | | | | | | | +| ExecutePipeline_V7 | 10 | 1,191.41 ns | 35.512 ns | 53.152 ns | 1,192.79 ns | 1.00 | 0.00 | 0.3014 | 2536 B | 1.00 | +| ExecutePipeline_V8 | 10 | 557.95 ns | 76.434 ns | 112.036 ns | 505.58 ns | 0.47 | 0.09 | - | - | 0.00 | ## TIMEOUT -| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|------------------ |---------:|--------:|---------:|------:|--------:|-------:|----------:|------------:| -| ExecuteTimeout_V7 | 287.1 ns | 9.20 ns | 12.59 ns | 1.00 | 0.00 | 0.0868 | 728 B | 1.00 | -| ExecuteTimeout_V8 | 272.9 ns | 3.16 ns | 4.54 ns | 0.95 | 0.04 | 0.0439 | 368 B | 0.51 | +| 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 | ## RETRY | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | |---------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:| -| ExecuteRetry_V7 | 162.5 ns | 2.33 ns | 3.34 ns | 1.00 | 0.00 | 0.0687 | 576 B | 1.00 | -| ExecuteRetry_V8 | 152.3 ns | 1.31 ns | 1.93 ns | 0.94 | 0.02 | 0.0181 | 152 B | 0.26 | +| ExecuteRetry_V7 | 169.8 ns | 4.98 ns | 6.98 ns | 1.00 | 0.00 | 0.0687 | 576 B | 1.00 | +| ExecuteRetry_V8 | 144.9 ns | 2.35 ns | 3.52 ns | 0.85 | 0.04 | - | - | 0.00 | ## RATE LIMITER -| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|---------------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:| -| ExecuteRateLimiter_V7 | 173.8 ns | 2.33 ns | 3.48 ns | 1.00 | 0.00 | 0.0448 | 376 B | 1.00 | -| ExecuteRateLimiter_V8 | 207.9 ns | 2.06 ns | 2.89 ns | 1.19 | 0.03 | 0.0229 | 192 B | 0.51 | +| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | +|---------------------- |---------:|---------:|---------:|------:|--------:|-------:|----------:|------------:| +| ExecuteRateLimiter_V7 | 190.8 ns | 10.01 ns | 14.98 ns | 1.00 | 0.00 | 0.0448 | 376 B | 1.00 | +| ExecuteRateLimiter_V8 | 199.6 ns | 2.54 ns | 3.64 ns | 1.05 | 0.09 | 0.0048 | 40 B | 0.11 | ## STRATEGY PIPELINE (TIMEOUT + RETRY + TIMEOUT + RATE LIMITER) -| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|--------------------------- |---------:|----------:|----------:|---------:|------:|--------:|-------:|----------:|------------:| -| ExecuteStrategyPipeline_V7 | 1.207 us | 0.0201 us | 0.0295 us | 1.202 us | 1.00 | 0.00 | 0.2861 | 2400 B | 1.00 | -| ExecuteStrategyPipeline_V8 | 1.117 us | 0.0297 us | 0.0407 us | 1.118 us | 0.93 | 0.05 | 0.0935 | 792 B | 0.33 | \ No newline at end of file +| 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 | diff --git a/src/Polly.Core.Tests/ExecutionRejectedExceptionTests.cs b/src/Polly.Core.Tests/ExecutionRejectedExceptionTests.cs index 40f7047a649..a3051d9b4d2 100644 --- a/src/Polly.Core.Tests/ExecutionRejectedExceptionTests.cs +++ b/src/Polly.Core.Tests/ExecutionRejectedExceptionTests.cs @@ -1,11 +1,11 @@ -namespace Polly.Core.Tests.Timeout; +namespace Polly.Core.Tests; public class ExecutionRejectedExceptionTests { [Fact] public void Ctor_Ok() { - new CustomException().Message.Should().Be("Exception of type 'Polly.Core.Tests.Timeout.ExecutionRejectedExceptionTests+CustomException' was thrown."); + new CustomException().Message.Should().Be("Exception of type 'Polly.Core.Tests.ExecutionRejectedExceptionTests+CustomException' was thrown."); new CustomException("Dummy").Message.Should().Be("Dummy"); } diff --git a/src/Polly.Core.Tests/Utils/BinarySerializationUtil.cs b/src/Polly.Core.Tests/Helpers/BinarySerializationUtil.cs similarity index 94% rename from src/Polly.Core.Tests/Utils/BinarySerializationUtil.cs rename to src/Polly.Core.Tests/Helpers/BinarySerializationUtil.cs index e6df2674fec..99e657765af 100644 --- a/src/Polly.Core.Tests/Utils/BinarySerializationUtil.cs +++ b/src/Polly.Core.Tests/Helpers/BinarySerializationUtil.cs @@ -2,7 +2,7 @@ using System; -namespace Polly.Core.Tests.Utils; +namespace Polly.Core.Tests.Helpers; /// /// Utility that serializes and deserializes the exceptions using the binary formatter. diff --git a/src/Polly.Core.Tests/Utils/FakeTimeProvider.cs b/src/Polly.Core.Tests/Helpers/FakeTimeProvider.cs similarity index 97% rename from src/Polly.Core.Tests/Utils/FakeTimeProvider.cs rename to src/Polly.Core.Tests/Helpers/FakeTimeProvider.cs index 3a0a2c55b03..f781c810e14 100644 --- a/src/Polly.Core.Tests/Utils/FakeTimeProvider.cs +++ b/src/Polly.Core.Tests/Helpers/FakeTimeProvider.cs @@ -1,7 +1,7 @@ using Moq; using Polly.Utils; -namespace Polly.Core.Tests.Utils; +namespace Polly.Core.Tests.Helpers; internal class FakeTimeProvider : Mock { diff --git a/src/Polly.Core.Tests/Utils/TestArguments.cs b/src/Polly.Core.Tests/Helpers/TestArguments.cs similarity index 84% rename from src/Polly.Core.Tests/Utils/TestArguments.cs rename to src/Polly.Core.Tests/Helpers/TestArguments.cs index c1d4d0166c4..e419cc2cb31 100644 --- a/src/Polly.Core.Tests/Utils/TestArguments.cs +++ b/src/Polly.Core.Tests/Helpers/TestArguments.cs @@ -1,6 +1,6 @@ using Polly.Strategy; -namespace Polly.Core.Tests.Utils; +namespace Polly.Core.Tests.Helpers; public class TestArguments : IResilienceArguments { diff --git a/src/Polly.Core.Tests/Utils/TestResilienceStrategy.cs b/src/Polly.Core.Tests/Helpers/TestResilienceStrategy.cs similarity index 96% rename from src/Polly.Core.Tests/Utils/TestResilienceStrategy.cs rename to src/Polly.Core.Tests/Helpers/TestResilienceStrategy.cs index 2910db8fd2b..ba89f3b6324 100644 --- a/src/Polly.Core.Tests/Utils/TestResilienceStrategy.cs +++ b/src/Polly.Core.Tests/Helpers/TestResilienceStrategy.cs @@ -1,4 +1,4 @@ -namespace Polly.Core.Tests.Utils; +namespace Polly.Core.Tests.Helpers; public class TestResilienceStrategy : ResilienceStrategy { diff --git a/src/Polly.Core.Tests/Utils/TestUtils.cs b/src/Polly.Core.Tests/Helpers/TestUtils.cs similarity index 96% rename from src/Polly.Core.Tests/Utils/TestUtils.cs rename to src/Polly.Core.Tests/Helpers/TestUtils.cs index d24b4b9a611..dfb9f6d5c85 100644 --- a/src/Polly.Core.Tests/Utils/TestUtils.cs +++ b/src/Polly.Core.Tests/Helpers/TestUtils.cs @@ -1,4 +1,4 @@ -namespace Polly.Core.Tests.Utils; +namespace Polly.Core.Tests.Helpers; #pragma warning disable CA1031 // Do not catch general exception types diff --git a/src/Polly.Core.Tests/Polly.Core.Tests.csproj b/src/Polly.Core.Tests/Polly.Core.Tests.csproj index 477effa9186..7961beb4695 100644 --- a/src/Polly.Core.Tests/Polly.Core.Tests.csproj +++ b/src/Polly.Core.Tests/Polly.Core.Tests.csproj @@ -16,6 +16,6 @@ - + diff --git a/src/Polly.Core.Tests/Registry/StrategyId.cs b/src/Polly.Core.Tests/Registry/StrategyId.cs index 738f52cd906..97d71f34372 100644 --- a/src/Polly.Core.Tests/Registry/StrategyId.cs +++ b/src/Polly.Core.Tests/Registry/StrategyId.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace Polly.Core.Tests.Registry; public record StrategyId(Type Type, string BuilderName, string InstanceName = "") diff --git a/src/Polly.Core.Tests/ResilienceContextTests.cs b/src/Polly.Core.Tests/ResilienceContextTests.cs index accb8028189..3f362d6b5af 100644 --- a/src/Polly.Core.Tests/ResilienceContextTests.cs +++ b/src/Polly.Core.Tests/ResilienceContextTests.cs @@ -16,6 +16,16 @@ public void Get_EnsureDefaults() AssertDefaults(context); } + [Fact] + public void Get_EnsurePooled() + { + var context = ResilienceContext.Get(); + + ResilienceContext.Return(context); + + ResilienceContext.Get().Should().BeSameAs(context); + } + [Fact] public void Return_Null_Throws() { diff --git a/src/Polly.Core.Tests/Retry/RetryDelayGeneratorTests.cs b/src/Polly.Core.Tests/Retry/RetryDelayGeneratorTests.cs index 803136692f4..db6a0c7f6fd 100644 --- a/src/Polly.Core.Tests/Retry/RetryDelayGeneratorTests.cs +++ b/src/Polly.Core.Tests/Retry/RetryDelayGeneratorTests.cs @@ -1,8 +1,5 @@ -using System; -using System.Threading.Tasks; using Polly.Retry; using Polly.Strategy; -using Xunit; namespace Polly.Core.Tests.Retry; diff --git a/src/Polly.Core.Tests/Retry/RetryHelperTests.cs b/src/Polly.Core.Tests/Retry/RetryHelperTests.cs index ff2896da8e5..db2e7b126c4 100644 --- a/src/Polly.Core.Tests/Retry/RetryHelperTests.cs +++ b/src/Polly.Core.Tests/Retry/RetryHelperTests.cs @@ -1,4 +1,3 @@ -using System; using Polly.Retry; namespace Polly.Core.Tests.Retry; diff --git a/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs b/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs index 0dcf3f0cfe6..14560cf42d5 100644 --- a/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs +++ b/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using Polly.Builder; using Polly.Retry; -using Xunit; namespace Polly.Core.Tests.Retry; diff --git a/src/Polly.Core.Tests/Strategy/OutcomeEventTests.cs b/src/Polly.Core.Tests/Strategy/OutcomeEventTests.cs index 0fa72b80dfc..d05e8632b12 100644 --- a/src/Polly.Core.Tests/Strategy/OutcomeEventTests.cs +++ b/src/Polly.Core.Tests/Strategy/OutcomeEventTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Polly.Strategy; namespace Polly.Core.Tests.Strategy; diff --git a/src/Polly.Core.Tests/Strategy/OutcomeGeneratorTests.cs b/src/Polly.Core.Tests/Strategy/OutcomeGeneratorTests.cs index 29909c68eed..5db1c6b55c7 100644 --- a/src/Polly.Core.Tests/Strategy/OutcomeGeneratorTests.cs +++ b/src/Polly.Core.Tests/Strategy/OutcomeGeneratorTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Polly.Strategy; namespace Polly.Core.Tests.Strategy; diff --git a/src/Polly.Core.Tests/Strategy/OutcomePredicateTests.cs b/src/Polly.Core.Tests/Strategy/OutcomePredicateTests.cs index 0d968983519..b38d83480dd 100644 --- a/src/Polly.Core.Tests/Strategy/OutcomePredicateTests.cs +++ b/src/Polly.Core.Tests/Strategy/OutcomePredicateTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Polly.Strategy; namespace Polly.Core.Tests.Strategy; diff --git a/src/Polly.Core.Tests/Strategy/OutcomeTests.cs b/src/Polly.Core.Tests/Strategy/OutcomeTests.cs index f706225c5b7..1b0086c1e0d 100644 --- a/src/Polly.Core.Tests/Strategy/OutcomeTests.cs +++ b/src/Polly.Core.Tests/Strategy/OutcomeTests.cs @@ -1,4 +1,3 @@ -using System; using Polly.Strategy; namespace Polly.Core.Tests.Strategy; diff --git a/src/Polly.Core.Tests/Strategy/SimpleEventTests.cs b/src/Polly.Core.Tests/Strategy/SimpleEventTests.cs index 8bbafa0cbcc..f5732bc0f8e 100644 --- a/src/Polly.Core.Tests/Strategy/SimpleEventTests.cs +++ b/src/Polly.Core.Tests/Strategy/SimpleEventTests.cs @@ -1,6 +1,6 @@ using Polly.Strategy; -namespace Polly.Core.Tests.Timeout; +namespace Polly.Core.Tests.Strategy; public class SimpleEventTests { @@ -8,7 +8,7 @@ public class SimpleEventTests public async Task Add_EnsureOrdering() { var ev = new DummyEvent(); - List raisedEvents = new List(); + var raisedEvents = new List(); ev.Add(() => raisedEvents.Add(1)); ev.Add(args => raisedEvents.Add(2)); @@ -41,7 +41,7 @@ public async Task CreateHandler_Ok(int count) var ev = new DummyEvent(); var events = new List(); - for (int i = 0; i < count; i++) + for (var i = 0; i < count; i++) { ev.Add(() => events.Add(i)); } diff --git a/src/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs b/src/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs index 0137542f489..cee38631de1 100644 --- a/src/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs +++ b/src/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs @@ -1,4 +1,3 @@ -using System.ComponentModel.DataAnnotations; using Polly.Timeout; namespace Polly.Core.Tests.Timeout; diff --git a/src/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs b/src/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs index 9fd6902c520..1afca891179 100644 --- a/src/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs +++ b/src/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using Polly.Timeout; using Polly.Utils; -using Xunit; namespace Polly.Core.Tests.Timeout; diff --git a/src/Polly.Core.Tests/Timeout/TimeoutUtilTests.cs b/src/Polly.Core.Tests/Timeout/TimeoutUtilTests.cs index 801497dc7aa..202e861b525 100644 --- a/src/Polly.Core.Tests/Timeout/TimeoutUtilTests.cs +++ b/src/Polly.Core.Tests/Timeout/TimeoutUtilTests.cs @@ -1,6 +1,4 @@ -using System; using Polly.Timeout; -using Xunit; namespace Polly.Core.Tests.Timeout; public class TimeoutUtilTests diff --git a/src/Polly.Core.Tests/Utils/ObjectPoolTests.cs b/src/Polly.Core.Tests/Utils/ObjectPoolTests.cs new file mode 100644 index 00000000000..738c7080aec --- /dev/null +++ b/src/Polly.Core.Tests/Utils/ObjectPoolTests.cs @@ -0,0 +1,111 @@ +using Polly.Utils; + +namespace Polly.Core.Tests.Utils; + +public class ObjectPoolTests +{ + [Fact] + public void GetAnd_ReturnObject_SameInstance() + { + // Arrange + var pool = new ObjectPool(() => new object(), _ => true); + + var obj1 = pool.Get(); + pool.Return(obj1); + + // Act + var obj2 = pool.Get(); + + // Assert + Assert.Same(obj1, obj2); + } + + [Fact] + public void MaxCapacity_Ok() + { + ObjectPool.MaxCapacity.Should().Be((Environment.ProcessorCount * 2) - 1); + } + + [Fact] + public void MaxCapacity_Respected() + { + // Arrange + var pool = new ObjectPool(() => new object(), _ => true); + var items1 = GetStoreReturn(pool); + + // Act + var items2 = GetStoreReturn(pool); + + // Assert + items1.Should().BeEquivalentTo(items2); + } + + [Fact] + public void MaxCapacityOverflow_Respected() + { + // Arrange + var count = ObjectPool.MaxCapacity + 10; + var pool = new ObjectPool(() => new object(), _ => true); + var items1 = GetStoreReturn(pool, count); + + // Act + var items2 = GetStoreReturn(pool, count); + + // Assert + items1.Last().Should().NotBeSameAs(items2.Last()); + } + + [Fact] + public void CreatedByPolicy() + { + // Arrange + var policy = new ListPolicy(); + var pool = new ObjectPool>(ListPolicy.Create, ListPolicy.Return); + + // Act + var list = pool.Get(); + + // Assert + Assert.Equal(17, list.Capacity); + } + + [Fact] + public void Return_RejectedByPolicy() + { + // Arrange + var policy = new ListPolicy(); + var pool = new ObjectPool>(ListPolicy.Create, ListPolicy.Return); + var list1 = pool.Get(); + list1.Capacity = 20; + + // Act + pool.Return(list1); + var list2 = pool.Get(); + + // Assert + Assert.NotSame(list1, list2); + } + + private static List GetStoreReturn(ObjectPool pool, int? count = null) + { + var items = new List(); + for (int i = 0; i < (count ?? ObjectPool.MaxCapacity); i++) + { + items.Add(pool.Get()); + } + + foreach (var item in items) + { + pool.Return(item); + } + + return items; + } + + private class ListPolicy + { + public static List Create() => new(17); + + public static bool Return(List obj) => obj.Capacity == 17; + } +} diff --git a/src/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs b/src/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs index 02430df73ef..c421cf9b33a 100644 --- a/src/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs +++ b/src/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; namespace Polly.Core.Tests.Utils; diff --git a/src/Polly.Core.Tests/Utils/ValidationContextExtensions.cs b/src/Polly.Core.Tests/Utils/ValidationContextExtensionsTests.cs similarity index 95% rename from src/Polly.Core.Tests/Utils/ValidationContextExtensions.cs rename to src/Polly.Core.Tests/Utils/ValidationContextExtensionsTests.cs index 962da7c14c0..82fecffadc1 100644 --- a/src/Polly.Core.Tests/Utils/ValidationContextExtensions.cs +++ b/src/Polly.Core.Tests/Utils/ValidationContextExtensionsTests.cs @@ -2,7 +2,7 @@ namespace Polly.Core.Tests.Utils; -public class ValidationContextExtensions +public class ValidationContextExtensionsTests { [Fact] public void GetMemberName_Ok() diff --git a/src/Polly.Core/ResilienceContext.cs b/src/Polly.Core/ResilienceContext.cs index bc53c78de6f..d92bc27d1db 100644 --- a/src/Polly.Core/ResilienceContext.cs +++ b/src/Polly.Core/ResilienceContext.cs @@ -12,7 +12,9 @@ namespace Polly; /// public sealed class ResilienceContext { - internal const bool ContinueOnCapturedContextDefault = false; + private const bool ContinueOnCapturedContextDefault = false; + + private static readonly ObjectPool Pool = new(static () => new ResilienceContext(), static c => c.Reset()); private ResilienceContext() { @@ -61,7 +63,7 @@ private ResilienceContext() /// After the execution is finished you should return the back to the pool /// by calling method. /// - public static ResilienceContext Get() => new(); + public static ResilienceContext Get() => Pool.Get(); /// /// Returns a back to the pool. @@ -71,7 +73,7 @@ public static void Return(ResilienceContext context) { Guard.NotNull(context); - context.Reset(); + Pool.Return(context); } [ExcludeFromCodeCoverage] @@ -90,13 +92,15 @@ internal ResilienceContext Initialize(bool isSynchronous) return this; } - private void Reset() + private bool Reset() { IsSynchronous = false; ResultType = typeof(UnknownResult); ContinueOnCapturedContext = false; CancellationToken = default; ((IDictionary)Properties).Clear(); + + return true; } /// diff --git a/src/Polly.Core/Utils/ObjectPool.cs b/src/Polly.Core/Utils/ObjectPool.cs new file mode 100644 index 00000000000..ac59352dacc --- /dev/null +++ b/src/Polly.Core/Utils/ObjectPool.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Polly.Utils; + +// copied from https://raw.githubusercontent.com/dotnet/aspnetcore/53124ab8cbf152f59120982f1c74e802e7970845/src/ObjectPool/src/DefaultObjectPool.cs +internal sealed class ObjectPool + where T : class +{ + internal static readonly int MaxCapacity = (Environment.ProcessorCount * 2) - 1; // the - 1 is to account for _fastItem + + private readonly Func _createFunc; + private readonly Func _returnFunc; + + private readonly ConcurrentQueue _items = new(); + + private T? _fastItem; + private int _numItems; + + public ObjectPool(Func createFunc, Func returnFunc) + { + _createFunc = createFunc; + _returnFunc = returnFunc; + } + + public T Get() + { + var item = _fastItem; + if (item == null || Interlocked.CompareExchange(ref _fastItem, null, item) != item) + { + if (_items.TryDequeue(out item)) + { + Interlocked.Decrement(ref _numItems); + return item; + } + + // no object available, so go get a brand new one + return _createFunc(); + } + + return item; + } + + public void Return(T obj) + { + if (!_returnFunc(obj)) + { + // policy says to drop this object + return; + } + + // Stryker disable once equality : no means to test this + if (_fastItem != null || Interlocked.CompareExchange(ref _fastItem, obj, null) != null) + { + // Stryker disable once equality : no means to test this + if (Interlocked.Increment(ref _numItems) <= MaxCapacity) + { + _items.Enqueue(obj); + return; + } + + // no room, clean up the count and drop the object on the floor + Interlocked.Decrement(ref _numItems); + } + } +}