From 846d30ccf83179f2d6601176b94a0931f040e826 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 28 Jul 2021 15:52:14 +0800 Subject: [PATCH 1/3] Allowed to override CancellationToken for a specific call --- framework/Volo.Abp.sln | 7 +++ .../HttpContextCancellationTokenProvider.cs | 21 +++++-- .../Threading/CancellationTokenOverride.cs | 14 +++++ .../CancellationTokenProviderBase.cs | 26 ++++++++ .../Threading/ICancellationTokenProvider.cs | 4 +- .../NullCancellationTokenProvider.cs | 8 +-- .../Volo.Abp.Threading.Tests.csproj | 16 +++++ .../Abp/Threading/AbpThreadingTestModule.cs | 15 +++++ .../NullCancellationTokenProvider_Tests.cs | 60 +++++++++++++++++++ 9 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenOverride.cs create mode 100644 framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderBase.cs create mode 100644 framework/test/Volo.Abp.Threading.Tests/Volo.Abp.Threading.Tests.csproj create mode 100644 framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/AbpThreadingTestModule.cs create mode 100644 framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 0605f5c103..8d5432f4c6 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -383,6 +383,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Scr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MongoDB.Tests.SecondContext", "test\Volo.Abp.MongoDB.Tests.SecondContext\Volo.Abp.MongoDB.Tests.SecondContext.csproj", "{90B1866A-EF99-40B9-970E-B898E5AA523F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Threading.Tests", "test\Volo.Abp.Threading.Tests\Volo.Abp.Threading.Tests.csproj", "{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1141,6 +1143,10 @@ Global {90B1866A-EF99-40B9-970E-B898E5AA523F}.Debug|Any CPU.Build.0 = Debug|Any CPU {90B1866A-EF99-40B9-970E-B898E5AA523F}.Release|Any CPU.ActiveCfg = Release|Any CPU {90B1866A-EF99-40B9-970E-B898E5AA523F}.Release|Any CPU.Build.0 = Release|Any CPU + {7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1334,6 +1340,7 @@ Global {C996F458-98FB-483D-9306-4701290E2FC1} = {447C8A77-E5F0-4538-8687-7383196D04EA} {75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C} = {447C8A77-E5F0-4538-8687-7383196D04EA} {90B1866A-EF99-40B9-970E-B898E5AA523F} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Threading/HttpContextCancellationTokenProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Threading/HttpContextCancellationTokenProvider.cs index c7c961176c..ce722b1176 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Threading/HttpContextCancellationTokenProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Threading/HttpContextCancellationTokenProvider.cs @@ -6,13 +6,26 @@ using Volo.Abp.Threading; namespace Volo.Abp.AspNetCore.Threading { [Dependency(ReplaceServices = true)] - public class HttpContextCancellationTokenProvider : ICancellationTokenProvider, ITransientDependency + public class HttpContextCancellationTokenProvider : CancellationTokenProviderBase, ITransientDependency { - public CancellationToken Token => _httpContextAccessor.HttpContext?.RequestAborted ?? CancellationToken.None; - private readonly IHttpContextAccessor _httpContextAccessor; - public HttpContextCancellationTokenProvider(IHttpContextAccessor httpContextAccessor) + public override CancellationToken Token + { + get + { + if (OverrideValue != null) + { + return OverrideValue.CancellationToken; + } + return _httpContextAccessor.HttpContext?.RequestAborted ?? CancellationToken.None; + } + } + + public HttpContextCancellationTokenProvider( + IAmbientScopeProvider cancellationTokenOverrideScopeProvider, + IHttpContextAccessor httpContextAccessor) + : base(cancellationTokenOverrideScopeProvider) { _httpContextAccessor = httpContextAccessor; } diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenOverride.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenOverride.cs new file mode 100644 index 0000000000..371e3d6652 --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenOverride.cs @@ -0,0 +1,14 @@ +using System.Threading; + +namespace Volo.Abp.Threading +{ + public class CancellationTokenOverride + { + public CancellationToken CancellationToken { get; } + + public CancellationTokenOverride(CancellationToken cancellationToken) + { + CancellationToken = cancellationToken; + } + } +} diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderBase.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderBase.cs new file mode 100644 index 0000000000..20101a95da --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderBase.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; + +namespace Volo.Abp.Threading +{ + public abstract class CancellationTokenProviderBase : ICancellationTokenProvider + { + public const string CancellationTokenOverrideContextKey = "Volo.Abp.Threading.CancellationToken.Override"; + + public abstract CancellationToken Token { get; } + + protected IAmbientScopeProvider CancellationTokenOverrideScopeProvider { get; } + + protected CancellationTokenOverride OverrideValue => CancellationTokenOverrideScopeProvider.GetValue(CancellationTokenOverrideContextKey); + + protected CancellationTokenProviderBase(IAmbientScopeProvider cancellationTokenOverrideScopeProvider) + { + CancellationTokenOverrideScopeProvider = cancellationTokenOverrideScopeProvider; + } + + public IDisposable Use(CancellationToken cancellationToken) + { + return CancellationTokenOverrideScopeProvider.BeginScope(CancellationTokenOverrideContextKey, new CancellationTokenOverride(cancellationToken)); + } + } +} diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs index 24e303adde..c270eeddd2 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs @@ -1,9 +1,11 @@ -using System.Threading; +using System; +using System.Threading; namespace Volo.Abp.Threading { public interface ICancellationTokenProvider { CancellationToken Token { get; } + IDisposable Use(CancellationToken cancellationToken); } } diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs index ab6320546c..6c2b4d364c 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs @@ -2,15 +2,15 @@ namespace Volo.Abp.Threading { - public class NullCancellationTokenProvider : ICancellationTokenProvider + public class NullCancellationTokenProvider : CancellationTokenProviderBase { public static NullCancellationTokenProvider Instance { get; } = new NullCancellationTokenProvider(); - public CancellationToken Token { get; } = CancellationToken.None; + public override CancellationToken Token => OverrideValue?.CancellationToken ?? CancellationToken.None; private NullCancellationTokenProvider() + : base(new AmbientDataContextAmbientScopeProvider(new AsyncLocalAmbientDataContext())) { - } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Threading.Tests/Volo.Abp.Threading.Tests.csproj b/framework/test/Volo.Abp.Threading.Tests/Volo.Abp.Threading.Tests.csproj new file mode 100644 index 0000000000..cbca0b1329 --- /dev/null +++ b/framework/test/Volo.Abp.Threading.Tests/Volo.Abp.Threading.Tests.csproj @@ -0,0 +1,16 @@ + + + + + + net5.0 + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/AbpThreadingTestModule.cs b/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/AbpThreadingTestModule.cs new file mode 100644 index 0000000000..de530512d3 --- /dev/null +++ b/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/AbpThreadingTestModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Threading +{ + [DependsOn( + typeof(AbpThreadingModule), + typeof(AbpTestBaseModule) + )] + public class AbpThreadingTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + } + } +} diff --git a/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs b/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs new file mode 100644 index 0000000000..24bc57dac7 --- /dev/null +++ b/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Threading +{ + public class NullCancellationTokenProvider_Tests : AbpIntegratedTest + { + private readonly ICancellationTokenProvider _cancellationTokenProvider; + + public NullCancellationTokenProvider_Tests() + { + _cancellationTokenProvider = NullCancellationTokenProvider.Instance; + } + + [Fact] + public void Should_Return_None_Token() + { + _cancellationTokenProvider.Token.ShouldBe(CancellationToken.None); + } + + [Fact] + public void Should_Return_Specific_Token() + { + var cts = new CancellationTokenSource(); + + using (_cancellationTokenProvider.Use(cts.Token)) + { + _cancellationTokenProvider.Token.ShouldBe(cts.Token); + } + + _cancellationTokenProvider.Token.ShouldBe(CancellationToken.None); + } + + [Fact] + public void Should_Cancel_After_100_Milliseconds() + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromMilliseconds(100)); + + using (_cancellationTokenProvider.Use(cts.Token)) + { + Should.Throw(() => LongTask(1000, _cancellationTokenProvider.Token)); + } + } + + private void LongTask(int loopCounter, CancellationToken cancellationToken = default) + { + for (var i = 0; i < loopCounter; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + Thread.Sleep(10); + } + } + } +} From 121280829d73705294ddceec28e024c73ad792f7 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 28 Jul 2021 16:33:13 +0800 Subject: [PATCH 2/3] Update ICancellationTokenProvider.cs --- .../Volo/Abp/Threading/ICancellationTokenProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs index c270eeddd2..06dc008d52 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/ICancellationTokenProvider.cs @@ -6,6 +6,7 @@ namespace Volo.Abp.Threading public interface ICancellationTokenProvider { CancellationToken Token { get; } + IDisposable Use(CancellationToken cancellationToken); } } From d9aab07b928b32381ce92f0ff167918b0f697d2c Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 28 Jul 2021 21:04:34 +0800 Subject: [PATCH 3/3] Update NullCancellationTokenProvider_Tests.cs --- .../Abp/Threading/NullCancellationTokenProvider_Tests.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs b/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs index 24bc57dac7..34c5988eb1 100644 --- a/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs +++ b/framework/test/Volo.Abp.Threading.Tests/Volo/Abp/Threading/NullCancellationTokenProvider_Tests.cs @@ -29,7 +29,9 @@ namespace Volo.Abp.Threading using (_cancellationTokenProvider.Use(cts.Token)) { - _cancellationTokenProvider.Token.ShouldBe(cts.Token); + var newCancellationTokenProvider = NullCancellationTokenProvider.Instance; + + newCancellationTokenProvider.Token.ShouldBe(cts.Token); } _cancellationTokenProvider.Token.ShouldBe(CancellationToken.None); @@ -43,7 +45,8 @@ namespace Volo.Abp.Threading using (_cancellationTokenProvider.Use(cts.Token)) { - Should.Throw(() => LongTask(1000, _cancellationTokenProvider.Token)); + var newCancellationTokenProvider = NullCancellationTokenProvider.Instance; + Should.Throw(() => LongTask(1000, newCancellationTokenProvider.Token)); } }