diff --git a/src/Common/tests/System/Threading/ThreadTestHelpers.cs b/src/Common/tests/System/Threading/ThreadTestHelpers.cs index 316fb1b9af9f..26e9fba5c34f 100644 --- a/src/Common/tests/System/Threading/ThreadTestHelpers.cs +++ b/src/Common/tests/System/Threading/ThreadTestHelpers.cs @@ -107,6 +107,11 @@ public static void RunTestInBackgroundThread(Action test) waitForThread(); } + public static void RunTestInBackgroundThread(Func test) + { + RunTestInBackgroundThread(() => test().Wait()); + } + public static void WaitForCondition(Func condition) { WaitForConditionWithCustomDelay(condition, () => Thread.Sleep(1)); diff --git a/src/System.Threading.Thread/src/System/Threading/Thread.cs b/src/System.Threading.Thread/src/System/Threading/Thread.cs index 0be10726c2f6..e9e654a60233 100644 --- a/src/System.Threading.Thread/src/System/Threading/Thread.cs +++ b/src/System.Threading.Thread/src/System/Threading/Thread.cs @@ -15,12 +15,12 @@ public sealed partial class Thread : CriticalFinalizerObject { [ThreadStatic] private static Thread t_currentThread; + private static AsyncLocal s_asyncLocalPrincipal; private readonly RuntimeThread _runtimeThread; private Delegate _start; private CultureInfo _startCulture; private CultureInfo _startUICulture; - private IPrincipal _principal; private Thread(RuntimeThread runtimeThread) { @@ -190,11 +190,23 @@ public static IPrincipal CurrentPrincipal { get { - return CurrentThread._principal ?? (CurrentThread._principal = AppDomain.CurrentDomain.GetThreadPrincipal()); + if (s_asyncLocalPrincipal is null) + { + CurrentPrincipal = AppDomain.CurrentDomain.GetThreadPrincipal(); + } + return s_asyncLocalPrincipal?.Value; } set { - CurrentThread._principal = value; + if (s_asyncLocalPrincipal is null) + { + if (value is null) + { + return; + } + Interlocked.CompareExchange(ref s_asyncLocalPrincipal, new AsyncLocal(), null); + } + s_asyncLocalPrincipal.Value = value; } } diff --git a/src/System.Threading.Thread/tests/ThreadTests.cs b/src/System.Threading.Thread/tests/ThreadTests.cs index 4559b4b2a50a..facea368d6d2 100644 --- a/src/System.Threading.Thread/tests/ThreadTests.cs +++ b/src/System.Threading.Thread/tests/ThreadTests.cs @@ -7,7 +7,9 @@ using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; +using System.Security.Claims; using System.Security.Principal; +using System.Threading.Tasks; using System.Threading.Tests; using Xunit; @@ -427,6 +429,97 @@ public static void CurrentPrincipalTest() }); } + [Fact] + public static void CurrentPrincipalContextFlowTest() + { + ThreadTestHelpers.RunTestInBackgroundThread(async () => + { + Thread.CurrentPrincipal = new ClaimsPrincipal(); + + await Task.Run(async() => { + + Assert.IsType(Thread.CurrentPrincipal); + + await Task.Run(async() => + { + Assert.IsType(Thread.CurrentPrincipal); + + Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("name"), new string[0]); + + await Task.Run(() => + { + Assert.IsType(Thread.CurrentPrincipal); + }); + + Assert.IsType(Thread.CurrentPrincipal); + }); + + Assert.IsType(Thread.CurrentPrincipal); + }); + + Assert.IsType(Thread.CurrentPrincipal); + }); + } + + [Fact] + public static void CurrentPrincipalContextFlowTest_NotFlow() + { + ThreadTestHelpers.RunTestInBackgroundThread(async () => + { + Thread.CurrentPrincipal = new ClaimsPrincipal(); + + Task task; + using(ExecutionContext.SuppressFlow()) + { + Assert.True(ExecutionContext.IsFlowSuppressed()); + + task = Task.Run(() => + { + // Default PrincipalPolicy for netcoreapp is null + if (PlatformDetection.IsNetCore) + { + Assert.Null(Thread.CurrentPrincipal); + } + else + { + Assert.IsType(Thread.CurrentPrincipal); + } + + Assert.False(ExecutionContext.IsFlowSuppressed()); + }); + } + + Assert.False(ExecutionContext.IsFlowSuppressed()); + + await task; + }); + } + + [Fact] + public static void CurrentPrincipal_SetNull() + { + // We run test on remote process because we need to set same principal policy + // On netfx default principal policy is PrincipalPolicy.UnauthenticatedPrincipal + DummyClass.RemoteInvoke(() => + { + AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.NoPrincipal); + + Assert.Null(Thread.CurrentPrincipal); + + Thread.CurrentPrincipal = null; + Assert.Null(Thread.CurrentPrincipal); + + Thread.CurrentPrincipal = new ClaimsPrincipal(); + Assert.IsType(Thread.CurrentPrincipal); + + Thread.CurrentPrincipal = null; + Assert.Null(Thread.CurrentPrincipal); + + Thread.CurrentPrincipal = new ClaimsPrincipal(); + Assert.IsType(Thread.CurrentPrincipal); + }).Dispose(); + } + [Fact] public static void CurrentThreadTest() {