Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
5 changes: 5 additions & 0 deletions src/Common/tests/System/Threading/ThreadTestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ public static void RunTestInBackgroundThread(Action test)
waitForThread();
}

public static void RunTestInBackgroundThread(Func<Task> test)
{
RunTestInBackgroundThread(() => test().Wait());
}

public static void WaitForCondition(Func<bool> condition)
{
WaitForConditionWithCustomDelay(condition, () => Thread.Sleep(1));
Expand Down
18 changes: 15 additions & 3 deletions src/System.Threading.Thread/src/System/Threading/Thread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public sealed partial class Thread : CriticalFinalizerObject
{
[ThreadStatic]
private static Thread t_currentThread;
private static AsyncLocal<IPrincipal> s_asyncLocalPrincipal;

private readonly RuntimeThread _runtimeThread;
private Delegate _start;
private CultureInfo _startCulture;
private CultureInfo _startUICulture;
private IPrincipal _principal;

private Thread(RuntimeThread runtimeThread)
{
Expand Down Expand Up @@ -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<IPrincipal>(), null);
}
s_asyncLocalPrincipal.Value = value;
}
}

Expand Down
93 changes: 93 additions & 0 deletions src/System.Threading.Thread/tests/ThreadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<ClaimsPrincipal>(Thread.CurrentPrincipal);

await Task.Run(async() =>
{
Assert.IsType<ClaimsPrincipal>(Thread.CurrentPrincipal);

Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("name"), new string[0]);

await Task.Run(() =>
{
Assert.IsType<GenericPrincipal>(Thread.CurrentPrincipal);
});

Assert.IsType<GenericPrincipal>(Thread.CurrentPrincipal);
});

Assert.IsType<ClaimsPrincipal>(Thread.CurrentPrincipal);
});

Assert.IsType<ClaimsPrincipal>(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<GenericPrincipal>(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<ClaimsPrincipal>(Thread.CurrentPrincipal);

Thread.CurrentPrincipal = null;
Assert.Null(Thread.CurrentPrincipal);

Thread.CurrentPrincipal = new ClaimsPrincipal();
Assert.IsType<ClaimsPrincipal>(Thread.CurrentPrincipal);
}).Dispose();
}

[Fact]
public static void CurrentThreadTest()
{
Expand Down