Bug 24757 - LogicalCallContext not flowing with async calls
Summary: LogicalCallContext not flowing with async calls
Alias: None
Product: Class Libraries
Classification: Mono
Component: mscorlib ()
Version: 3.8.0
Hardware: PC Linux
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
Depends on:
Reported: 2014-11-23 10:20 UTC by pzavolinsky
Modified: 2014-11-26 13:03 UTC (History)
2 users (show)

Is this bug a regression?: ---
Last known good build:

Repro code (mcs MonoReproLogicalCallContext.cs; mono MonoReproLogicalCallContext.exe) (590 bytes, text/x-csharp)
2014-11-23 10:20 UTC, pzavolinsky

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description pzavolinsky 2014-11-23 10:20:29 UTC
Created attachment 8844 [details]
Repro code (mcs MonoReproLogicalCallContext.cs; mono MonoReproLogicalCallContext.exe)

Trying to implement this http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html works in Microsoft .NET but fails in Mono 3.10.0. I'm attaching a simplified repro code.

Essentially the following snippet does not behave as expected (i.e. output "A == A" and "B == B", in any order):
  public static void Main(string[] args)
    Task.WhenAll(Work("A"), Work("B")).Wait();
  public static async Task Work(string name)
    CallContext.LogicalSetData("name", name);
    await Task.Delay(10);
    Console.WriteLine("{0} == {1}", name, CallContext.LogicalGetData("name"));

The repro code in Mono 3.1.0 outputs:
A == 
B == 

In Microsoft .NET 4.5 outputs (as expected):
A == A
B == B

$ mono --version
Mono JIT compiler version 3.10.0 (tarball Wed Nov  5 12:50:04 UTC 2014)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  amd64
        Disabled:      none
        Misc:          softdebug 
        LLVM:          supported, not enabled.
        GC:            sgen

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 12.04.5 LTS
Release:        12.04
Codename:       precise

$ cat /proc/cpuinfo | grep 'model name' | uniq
model name      : Intel(R) Core(TM) i5-3317U CPU @ 1.70GHz

$ dpkg -l | grep mono-mcs
ii  mono-mcs                                                    3.10.0-0xamarin2                                    Mono C# 2.0 / 3.0 / 4.0 / 5.0  compiler for CLI 2.0 / 4.0 / 4.5

I did some poking around the code and it looks like the ExecutionContext captured by the System.Threading.Tasks.Task.ctor is not restored previous to the continuation execution (i.e. ExecutionContext.Run is never called). I've check this by building from source and adding Console.WriteLines in the Caputre and Run lines.

Here is the stack trace after the Console.WriteLine in the Work() method:
   at MonoReproLogicalCallContext.MainClass+<Work>c__async0.MoveNext()
   at System.Threading.Tasks.AwaiterActionContinuation.Execute()
   at System.Threading.Tasks.Task.ProcessCompleteDelegates()
   at System.Threading.Tasks.Task.Finish()
   at System.Threading.Tasks.Task.<Delay>m__2(System.Object state)
   at System.Threading.Timer+Scheduler.TimerCB(System.Object o)

After running the sample code many times I had a different (yet still invalid output):
A == A
B ==

The working task (A) produced this stack trace:
   at MonoReproLogicalCallContext.MainClass+<Work>c__async0.MoveNext()
   at System.Threading.Tasks.TaskActionInvoker+ActionInvoke.Invoke(System.Threading.Tasks.Task owner, System.Object state, System.Threading.Tasks.Task context)
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.<ThreadStart>m__1(System.Object l)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state)
   at System.Threading.Tasks.Task.ThreadStart()
   at System.Threading.Tasks.Task.Execute()
   at System.Threading.Tasks.TpScheduler.TaskExecuterCallback(System.Object obj)

In some cases ExecutionContext.Run does get called and everything works as expected, so maybe this an optimization side effect.
Comment 1 Marek Safar 2014-11-26 13:03:55 UTC
Fixed in master