Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can SynchronizationContext.Current of the main thread become null in a Windows Forms application?

I have a problem in my application: At some point, the SynchronizationContext.Current becomes null for the main thread. I'm unable to reproduce the same problem in an isolated project. My real project is complex; it mixes Windows Forms and WPF and calls WCF Web Services. As far as I know, those are all systems that may interact with the SynchronizationContext.

This is the code from my isolated project. My real app does something that resembles that. However, in my real app the SynchronizationContext.Current is null on the main thread when the continuation task is executed.

private void button2_Click(object sender, EventArgs e) {     if (SynchronizationContext.Current == null)     {         Debug.Fail("SynchronizationContext.Current is null");     }      Task.Factory.StartNew(() =>     {         CallWCFWebServiceThatThrowsAnException();     })     .ContinueWith((t) =>     {          //update the UI         UpdateGUI(t.Exception);          if (SynchronizationContext.Current == null)         {             Debug.Fail("SynchronizationContext.Current is null");         }      }, CancellationToken.None,         TaskContinuationOptions.OnlyOnFaulted,        TaskScheduler.FromCurrentSynchronizationContext()); } 

What could cause the SynchronizationContext.Current of the main thread to become null?

Edit:

@Hans asked for the stack trace. Here it is:

     at MyApp.Framework.UI.Commands.AsyncCommand.HandleTaskError(Task task) in d:\sources\s2\Framework\Sources\UI\Commands\AsyncCommand.cs:line 157    at System.Threading.Tasks.Task.c__DisplayClassb.b__a(Object obj)    at System.Threading.Tasks.Task.InnerInvoke()    at System.Threading.Tasks.Task.Execute()    at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)    at System.Threading.ExecutionContext.runTryCode(Object userData)    at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)    at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)    at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)    at System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback(Object obj)    at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)    at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)    at System.Delegate.DynamicInvokeImpl(Object[] args)    at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)    at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)    at System.Threading.ExecutionContext.runTryCode(Object userData)    at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)    at System.Windows.Forms.Control.InvokeMarshaledCallbacks()    at System.Windows.Forms.Control.WndProc(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)    at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)    at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)    at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.Run(Form mainForm)    at MyApp.Framework.SharedUI.ApplicationBase.InternalStart() in d:\sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 190    at MyApp.Framework.SharedUI.ApplicationBase.Start() in d:\sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 118    at MyApp.App1.WinUI.HDA.Main() in d:\sources\s2\App1\Sources\WinUI\HDA.cs:line 63  
like image 510
Sylvain Avatar asked Jan 11 '11 15:01

Sylvain


1 Answers

Sly, I have run into the exact same behavior when a mixture of WPF, WCF, and TPL is used. The Main thread's current SynchronizationContext will become null in a few situations.

var context = SynchronizationContext.Current;  // if context is null, an exception of // The current SynchronizationContext may not be used as a TaskScheduler. // will be thrown TaskScheduler.FromCurrentSynchronizationContext(); 

According to this post on the msdn forums, this is a confirmed bug in the TPL in 4.0. A coworker is running on 4.5 and does not see this behavior.

We solved this by creating a TaskScheduler in a static singleton with the main thread using FromCurrentSynchronizationContext and then always reference that task scheduler when creating continuations. For example

Task task = Task.Factory.StartNew(() =>   {     // something   } ).ContinueWith(t =>   {     // ui stuff   }, TheSingleton.Current.UiTaskScheduler); 

This avoids the issue in the TPL on .net 4.0.

Update If you have .net 4.5 installed on your development machine, you will not see this issue even if you are targeting the 4.0 framework. Your users who only have 4.0 installed will still be affected.

like image 56
Dan Avatar answered Oct 12 '22 01:10

Dan