Should CurrentPrincipal carry across async+await method calls?

So I have this example code:


using System; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; namespace PrincipalTestConsoleApplication { public class Program { public static void Main() { ThreadPool.SetMinThreads(20, 20); ThreadPool.SetMaxThreads(20, 20); Console.WriteLine($"\n Started {DateTime.UtcNow}\n"); for (int i = 1; i <= 10; i++) { Lemming lemming = new Lemming(i); lemming.BumpedHead += OnLemmingBumpedHead; Thread thread = new Thread(lemming.March); thread.Start(); } Console.WriteLine("Press CTRL+C to quit."); Console.ReadLine(); } private static void OnLemmingBumpedHead(object sender, HeadBumpArgs e) { Console.WriteLine(e.Message); } } public class Lemming { private readonly int m_LemmingId; public Lemming(int id) { m_LemmingId = id; } /// <summary> /// Primary thread method for a lemming. /// </summary> public void March() { while (true) { MarchStepAsync().Wait(); } } /// <summary> /// Async method for marching a single lemming step. /// </summary> private async Task MarchStepAsync() { //prepare for this step Thread.CurrentPrincipal = new LemmingPrincipal(m_LemmingId); if (ReferenceEquals(SynchronizationContext.Current, null)) { SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext()); } //check for initial anomalies LemmingPrincipal principal = Thread.CurrentPrincipal as LemmingPrincipal; if (ReferenceEquals(principal, null)) { FireHeadBumpEvent($"Lemming #{m_LemmingId} immediately thinks his principal type is: {Thread.CurrentPrincipal?.GetType()}"); } else if (principal.LemmingId != m_LemmingId) { FireHeadBumpEvent($"Lemming #{m_LemmingId} immediately thinks he's lemming #{principal.LemmingId}"); } //wait await Task.Delay(TimeSpan.FromMilliseconds(500 + m_LemmingId)); //check for anomalies after wait principal = Thread.CurrentPrincipal as LemmingPrincipal; if (ReferenceEquals(principal, null)) { FireHeadBumpEvent($"Lemming #{m_LemmingId} bumped his head and thinks his principal type is: {Thread.CurrentPrincipal?.GetType()}"); } else if (principal.LemmingId != m_LemmingId) { FireHeadBumpEvent($"Lemming #{m_LemmingId} bumped his head and thinks he's lemming #{principal.LemmingId}"); } } public event EventHandler<HeadBumpArgs> BumpedHead; private void FireHeadBumpEvent(string message) { BumpedHead?.Invoke(this, new HeadBumpArgs { Message = $"{DateTime.UtcNow}: {message}" }); } } public class HeadBumpArgs : EventArgs { public string Message { get; set; } } public class CustomSynchronizationContext : SynchronizationContext { public override void OperationStarted() { base.OperationStarted(); } public override void Post(SendOrPostCallback d, object state) { var theThis = this; SendOrPostCallback newD = (s) => { d(s); if (ReferenceEquals(SynchronizationContext.Current, null)) { SynchronizationContext.SetSynchronizationContext(theThis); } }; base.Post(newD, state); } public override SynchronizationContext CreateCopy() { var toReturn = base.CreateCopy(); return toReturn; } public override void OperationCompleted() { base.OperationCompleted(); } public override void Send(SendOrPostCallback d, object state) { base.Send(d, state); } public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) { return base.Wait(waitHandles, waitAll, millisecondsTimeout); } } /// <summary> /// Custom principal class. /// </summary> [Serializable] public class LemmingPrincipal : IPrincipal { public int LemmingId { get; } public IIdentity Identity { get; } public LemmingPrincipal(int id) { LemmingId = id; Identity = new LemmingIdentity("Lemming" + id); } public bool IsInRole(string role) { return false; } } /// <summary> /// Identity for custom principal. /// </summary> [Serializable] public class LemmingIdentity : IIdentity { public string AuthenticationType => "Default"; public bool IsAuthenticated => false; public string Name { get; } public LemmingIdentity(string name) { Name = name; } } }

Basically, it runs "lemming" threads which repeatedly set up a custom Thread.CurrentPrincipal, await Task.Delay, and then check the principal after the await.

On windows, it runs fine with no problems found; each lemming thread's logical thread of execution keeps its principal just fine for minutes on end.

On mono on linux (and when I port the code into a simple xamarin iOS app), I get constant problems found, where the Thread.CurrentPrincipal is lost immediately after each await Task.Delay:


Started 11/8/2016 5:22:08 PM Press CTRL+C to quit. 11/8/2016 5:22:08 PM: Lemming #3 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #1 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #4 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #7 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #6 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #8 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #9 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #2 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #10 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:08 PM: Lemming #5 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:09 PM: Lemming #1 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:09 PM: Lemming #3 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:09 PM: Lemming #4 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:09 PM: Lemming #6 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal 11/8/2016 5:22:09 PM: Lemming #7 bumped his head and thinks his principal type is: System.Security.Principal.GenericPrincipal ...

Is my expectation of CurrentPrincipal incorrect and the windows case is just working by happenstance? Or is this another xamarin/mono bug?

Tagged:

Answers

Sign In or Register to comment.