Skip to content

Commit

Permalink
Add support for .NET 8 (#1144)
Browse files Browse the repository at this point in the history
- Add `net8.0` targets.
- Use .NET `TimeProvider` implementation.
- Use `FakeTimeProvider` for tests.
- Use new .NET 8 APIs where relevant.

Co-Authored-By: martintmk <[email protected]>
  • Loading branch information
martincostello and martintmk authored Nov 14, 2023
1 parent 74677bc commit a2559b1
Show file tree
Hide file tree
Showing 38 changed files with 234 additions and 891 deletions.
12 changes: 3 additions & 9 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<MicrosoftExtensionsVersion>7.0.0</MicrosoftExtensionsVersion>
<MicrosoftExtensionsVersion>8.0.0</MicrosoftExtensionsVersion>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<PollyVersion>8.1.0</PollyVersion>
</PropertyGroup>
Expand All @@ -10,6 +10,7 @@
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.3.3" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsVersion)" />
Expand All @@ -20,6 +21,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="MinVer" Version="4.3.0" />
Expand All @@ -41,12 +43,4 @@
<PackageVersion Include="xunit" Version="2.6.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework) == 'net6.0'">
<PackageVersion Update="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageVersion Update="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))">
<PackageVersion Update="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageVersion Update="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion bench/Polly.Benchmarks/Polly.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ProjectType>Benchmark</ProjectType>
<NoWarn>$(NoWarn);CA1822;SA1414;IDE0060</NoWarn>
Expand Down
2 changes: 1 addition & 1 deletion bench/Polly.Core.Benchmarks/Polly.Core.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
<RootNamespace>Polly</RootNamespace>
<ImplicitUsings>true</ImplicitUsings>
<ProjectType>Benchmark</ProjectType>
Expand Down
1 change: 1 addition & 0 deletions eng/Library.targets
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

<PropertyGroup Label="NuGet package validation">
<EnablePackageValidation Condition="'$(EnablePackageValidation)' == ''">true</EnablePackageValidation>
<!-- TODO Bump to 8.2.0 once published to NuGet.org -->
<PackageValidationBaselineVersion Condition="'$(PackageValidationBaselineVersion)' == ''">8.1.0</PackageValidationBaselineVersion>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,14 @@ private static bool IsDateTimeOverflow(DateTimeOffset utcNow, TimeSpan breakDura

private void EnsureNotDisposed()
{
#if NET8_0_OR_GREATER
ObjectDisposedException.ThrowIf(_disposed, this);
#else
if (_disposed)
{
throw new ObjectDisposedException(nameof(CircuitStateController<T>));
}
#endif
}

private void CloseCircuit_NeedsLock(Outcome<T> outcome, bool manual, ResilienceContext context, out Task? scheduledTask)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ internal sealed class ScheduledTaskExecutor : IDisposable

public void ScheduleTask(Func<Task> taskFactory, ResilienceContext context, out Task task)
{
#if NET8_0_OR_GREATER
ObjectDisposedException.ThrowIf(_disposed, this);
#else
if (_disposed)
{
throw new ObjectDisposedException(nameof(ScheduledTaskExecutor));
}
#endif

var source = new TaskCompletionSource<object>();
task = source.Task;
Expand Down
8 changes: 8 additions & 0 deletions src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ public async ValueTask DisposeAsync()

using var delayTaskCancellation = CancellationTokenSource.CreateLinkedTokenSource(PrimaryContext!.CancellationToken);

#if NET8_0_OR_GREATER
var delayTask = Task.Delay(hedgingDelay, _timeProvider, delayTaskCancellation.Token);
#else
var delayTask = _timeProvider.Delay(hedgingDelay, delayTaskCancellation.Token);
#endif
Task<Task> whenAnyHedgedTask = WaitForTaskCompetitionAsync();
var completedTask = await Task.WhenAny(whenAnyHedgedTask, delayTask).ConfigureAwait(ContinueOnCapturedContext);

Expand All @@ -133,7 +137,11 @@ public async ValueTask DisposeAsync()

// cancel the ongoing delay task
// Stryker disable once boolean : no means to test this
#if NET8_0_OR_GREATER
await delayTaskCancellation.CancelAsync().ConfigureAwait(ContinueOnCapturedContext);
#else
delayTaskCancellation.Cancel(throwOnFirstException: false);
#endif

await whenAnyHedgedTask.ConfigureAwait(ContinueOnCapturedContext);

Expand Down
3 changes: 2 additions & 1 deletion src/Polly.Core/Polly.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0;net472;net462</TargetFrameworks>
<TargetFrameworks>net8.0;net6.0;netstandard2.0;net472;net462</TargetFrameworks>
<AssemblyTitle>Polly.Core</AssemblyTitle>
<RootNamespace>Polly</RootNamespace>
<Nullable>enable</Nullable>
Expand Down Expand Up @@ -29,6 +29,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))" />
<PackageReference Include="Microsoft.Bcl.TimeProvider" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net8.0'))" />
<PackageReference Include="System.Threading.Tasks.Extensions" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))" />
<PackageReference Include="System.ValueTuple" Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'" />
<PackageReference Include="System.ComponentModel.Annotations" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))" />
Expand Down
3 changes: 3 additions & 0 deletions src/Polly.Core/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ Polly.CircuitBreaker.BreakDurationGeneratorArguments.FailureCount.get -> int
Polly.CircuitBreaker.BreakDurationGeneratorArguments.FailureRate.get -> double
Polly.CircuitBreaker.CircuitBreakerStrategyOptions<TResult>.BreakDurationGenerator.get -> System.Func<Polly.CircuitBreaker.BreakDurationGeneratorArguments, System.Threading.Tasks.ValueTask<System.TimeSpan>>?
Polly.CircuitBreaker.CircuitBreakerStrategyOptions<TResult>.BreakDurationGenerator.set -> void
Polly.ResiliencePipelineBuilderBase.TimeProvider.get -> System.TimeProvider?
Polly.ResiliencePipelineBuilderBase.TimeProvider.set -> void
Polly.StrategyBuilderContext.TimeProvider.get -> System.TimeProvider!
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private Builder CreateBuilder()
builder.InstanceName = _instanceName;
_configure(builder, context);

var timeProvider = builder.TimeProvider;
var timeProvider = builder.TimeProviderInternal;
var telemetry = new ResilienceStrategyTelemetry(
new ResilienceTelemetrySource(builder.Name, builder.InstanceName, null),
builder.TelemetryListener);
Expand Down
16 changes: 7 additions & 9 deletions src/Polly.Core/ResiliencePipelineBuilderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,12 @@ private protected ResiliencePipelineBuilderBase(ResiliencePipelineBuilderBase ot
public ResilienceContextPool? ContextPool { get; set; }

/// <summary>
/// Gets or sets a <see cref="System.TimeProvider"/> that is used by strategies that work with time.
/// Gets or sets a <see cref="TimeProvider"/> that is used by strategies that work with time.
/// </summary>
/// <remarks>
/// This property is internal until we switch to official System.TimeProvider.
/// </remarks>
/// <value>
/// The default value is <see cref="TimeProvider.System"/>.
/// The default value is <see langword="null"/> and unless set, <see cref="TimeProvider.System"/> will be used.
/// </value>
[Required]
internal TimeProvider TimeProvider { get; set; } = TimeProvider.System;
public TimeProvider? TimeProvider { get; set; }

/// <summary>
/// Gets or sets the <see cref="Polly.Telemetry.TelemetryListener"/> that is used by Polly to report resilience events.
Expand All @@ -91,6 +87,8 @@ private protected ResiliencePipelineBuilderBase(ResiliencePipelineBuilderBase ot
[EditorBrowsable(EditorBrowsableState.Never)]
public TelemetryListener? TelemetryListener { get; set; }

internal TimeProvider TimeProviderInternal => TimeProvider ?? TimeProvider.System;

/// <summary>
/// Gets the validator that is used for the validation.
/// </summary>
Expand Down Expand Up @@ -132,13 +130,13 @@ internal PipelineComponent BuildPipelineComponent()

var source = new ResilienceTelemetrySource(Name, InstanceName, null);

return PipelineComponentFactory.CreateComposite(components, new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProvider);
return PipelineComponentFactory.CreateComposite(components, new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProviderInternal);
}

private PipelineComponent CreateComponent(Entry entry)
{
var source = new ResilienceTelemetrySource(Name, InstanceName, entry.Options.Name);
var context = new StrategyBuilderContext(new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProvider);
var context = new StrategyBuilderContext(new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProviderInternal);

var strategy = entry.Factory(context);
strategy.Options = entry.Options;
Expand Down
2 changes: 1 addition & 1 deletion src/Polly.Core/StrategyBuilderContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ internal StrategyBuilderContext(ResilienceStrategyTelemetry telemetry, TimeProvi
/// <summary>
/// Gets the <see cref="TimeProvider"/> used by this strategy.
/// </summary>
internal TimeProvider TimeProvider { get; }
public TimeProvider TimeProvider { get; }
}
Loading

0 comments on commit a2559b1

Please sign in to comment.