I've been trying to track down the following issue in a Winforms application:
The SynchronizationContext.Current
is null in a task's continuation (i.e. .ContinueWith
) which is run on the main thread (I expect the the current synchronization context to be System.Windows.Forms.WindowsFormsSynchronizationContext
).
Here's the Winforms code demonstrating the issue:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TaskScheduler ts = TaskScheduler.FromCurrentSynchronizationContext(); // Get the UI task scheduler
// This line is required to see the issue (Removing this causes the problem to go away), since it changes the codeflow in
// \SymbolCache\src\source\.NET\4\DEVDIV_TFS\Dev10\Releases\RTMRel\ndp\clr\src\BCL\System\Threading\ExecutionContext.cs\1305376\ExecutionContext.cs
// at line 435
System.Diagnostics.Trace.CorrelationManager.StartLogicalOperation("LogicalOperation");
var task = Task.Factory.StartNew(() => { });
var cont = task.ContinueWith(MyContinueWith, CancellationToken.None, TaskContinuationOptions.None, ts);
System.Diagnostics.Trace.CorrelationManager.StopLogicalOperation();
}
void MyContinueWith(Task t)
{
if (SynchronizationContext.Current == null) // The current SynchronizationContext shouldn't be null here, but it is.
MessageBox.Show("SynchronizationContext.Current is null");
}
}
}
This is an issue for me since I attempt to use BackgroundWorker
from the continuation, and the BackgroundWorker will use the current SynchronizationContext for its events RunWorkerCompleted
and ProgressChanged
. Since the current SynchronizationContext is null when I kick off the BackgroundWorker, the events don't run on the main ui thread as I intend.
My question:
Is this a bug in Microsoft's code, or have I made a mistake somewhere?
Additional info:
- I'm using .Net 4.0 (I haven't yet tried this on .NET 4.5 RC)
- I can reproduce this on both Debug/Release on any of x86/x64/Any CPU (on an x64 machine).
- It reproduces consistently (I'd be interested if anyone can't reproduce it).
- I have legacy code that uses the BackgroundWorker--so I can't easily change over to not using BackgroundWorker
- I've confirmed that the code in
MyContinueWith
is running on the main ui thread. - I don't know precisely why the
StartLogicalOperation
call helps cause the issue, that's just what I narrowed it down to in my application.
The issue is fixed in .NET 4.5 RC (just tested it). So I assume it is a bug in .NET 4.0. Also, I'm guessing that these posts are referencing the same issue:
That's unfortunate. Now I have to consider workarounds.
Edit:
From debugging into the .Net source, I have a little better understanding of when the issue would reproduce. Here's some relevant code from ExecutionContext.cs:
The issue only reproduces when we get into the "else" clause which calls RunInternal. This is because the RunInternal ends up replacing the the ExecutionContext which has the effect of changing what the current SynchronizationContext:
So, for my specific case, it was because the line `executionContext.IsDefaultFTContext(ignoreSyncCtx)) returned false. Here's that code:
For me, that was returning false due to
_logicalCallContext.HasInfo
was true. Here's that code:For me, this was returning true because HasUserData was true. Here's that code:
For me, the m_DataStore would have items in it due to my call to
Diagnostics.Trace.CorrelationManager.StartLogicalOperation("LogicalOperation");
In summary, it looks like there are several different ways you could get the bug to reproduce. Hopefully, this example will serve to help others in determining if they are running into this same bug or not.