I have a parallel thread requiring to open a form using Form.Show()
or, worst case, Form.ShowDialog(owner)
. I use the classic WinForm pattern as:
if (owner != null && owner.InvokeRequired)
owner.Invoke(new Action(() => { xForm.Show(owner); })); // <<< INVOKE ACTION >>>
The code normally works except for very few sporadic cases and the bug seems to be unreproducible.
It seems that the called xForm.Show(owner)
blocks just before returning and the callee owner.Invoke()
still wait.
Could you help me in finding some definitive solution?
An interesting clue is that, attaching to the blocked executable using Visual Studio Debugger.... pressing [Pause] and after a while [Continue] the code unblocks and proceed correctly: this seems that using a different parallel thread may change some form semaphores.
Also note that my application uses a number of Thread
and also Tasks
for async/await
pattern: sometimes in the explicit Thread
there may be loops calling Application.DoEvents().
Could be the case that different thread calling Application.DoEvents()
will consume different events so the form lifecycle could be corrupted?
To debug the situation, I replaced the classic Invoke()
pattern using my own method App.Invoke()
: this starts a parallel System.Timer.Timers.Timer
: if the Invoke()
is not resolved, when the timer elapses all stacktraces are dumped by using Microsoft.Diagnostics.Runtime.DataTarget.CreateSnapshotAndAttach() .
Here below, the stack traces taken for an example: the PARALLEL Thred
requires to open a form containg a ProgressBar using Form.Show().
Thread: managedId='40', name='PARALLEL Thread'
\[HelperMethodFrame_1OBJ\] (System.Threading.WaitHandle.WaitOneNative)
System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)
System.Threading.WaitHandle.WaitOne(Int32, Boolean)
System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)
System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object\[\], Boolean)
System.Windows.Forms.Control.Invoke(System.Delegate, System.Object\[\]) \<\<\<\< INVOKE ACTION \>\>\>\>
MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
MY.UI.ShowProgressForm.ProgressReportingObject_ProgressChanged(System.Object, TITP.Automation.Abstraction.Utility.Progress.ProgressChangedEventArgs)
MY.UI.ProgressContext.RaiseProgressChanged(TITP.Automation.Abstraction.Utility.Progress.ProgressChangedEventArgs)
MY.UI.ProgressContext.set_LongOperationInProgress(Boolean)
MY.UI.ProgressContext.BeginLongOperation(System.String, System.String)
MY.UI.frmMain.AutoWorkCycle()
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
System.Threading.ThreadHelper.ThreadStart()
\[GCFrame\]
\[DebuggerU2MCatchHandlerFrame\]
Thread: managedId='1', name='UIThread'
[InlinedCallFrame] (System.Windows.Forms.SafeNativeMethods.ShowWindow)
[InlinedCallFrame] (System.Windows.Forms.SafeNativeMethods.ShowWindow)
DomainBoundILStubClass.IL_STUB_PInvoke(System.Runtime.InteropServices.HandleRef, Int32)
System.Windows.Forms.Control.SetVisibleCore(Boolean)
System.Windows.Forms.Form.SetVisibleCore(Boolean)
*System.Windows.Forms.Form.Show(System.Windows.Forms.IWin32Window)*
MY.UI.ShowProgressForm.<__tryShow>b__55_0() <<<< INVOKE ACTION >>>>
MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
MY.UI.ShowProgressForm.__tryShow()
MY.UI.ShowProgressForm.__addProgressBar(System.Object, System.Object)
MY.UI.ShowProgressForm+<>c__DisplayClass42_0.<AddProgressBar>b__0()
MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
MY.UI.ShowProgressForm.AddProgressBar(System.Object)
MY.UI.ShowProgressForm+<>c__DisplayClass70_0.<Context_Show>b__0()
MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
MY.UI.ShowProgressForm.Context_Show(System.Object)
MY.UI.ShowProgressForm.ProgressChangedInternal(System.Object, TITP.Automation.Abstraction.Utility.Progress.ProgressChangedEventArgs)
MY.UI.ShowProgressForm+<>c__DisplayClass38_0.<ProgressReportingObject_ProgressChanged>b__0()
[DebuggerU2MCatchHandlerFrame]
[HelperMethodFrame_PROTECTOBJ] (System.RuntimeMethodHandle.InvokeMethod)
System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
System.Delegate.DynamicInvokeImpl(System.Object[])
System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry)
System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(System.Object)
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry)
System.Windows.Forms.Control.InvokeMarshaledCallbacks()
System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef)
System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int64, Int32, Int64, Int64)
[InlinedCallFrame] (System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW)
[InlinedCallFrame] (System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW)
DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
MY.UI.Program.Main()
[GCFrame]
The lines marked with <<<< INVOKE ACTION >>>>
corresponds to the blocked lines in my code.
At the end it seems that SetVisibleCore()
in UiThread
and InternalWaitOne()
in PARALLEL thread
are locking toghether.
=== UPDATE ===
Let me add a further episode: while "PARALLEL Thread" is doing
while (flagExit))
Thread.Sleep(50);
the UiThread may receive a click on textbox to open a popup dialog
((TextBox)control4).Click += delegate(object s, EventArgs e)
{
...
frmNumericPad.OpenNumpad() =>
{
using (frmNumericPad xPad = new frmNumericPad())
{
if (xPad.ShowDialog() == DialogResult.OK)
return true;
else
return false;
}
}
}
This new case result in a freeze where the process dump returns:
Not Flagged 5924 1 Main Thread UITHREAD
System.Windows.Forms.dll!System.Windows.Forms.Control.SetVisibleCore
[External Code]
MYControls.dll!MYControls.frmNumericPad.OpenNumpad(System.Windows.Forms.TextBox control, string numberFormat, double? minValue, double? maxValue, bool canBeIncremental, bool canBeNull) Line 140
MYControls.dll!MYControls.__internal_SetTouchKeypadBehaviour.AnonymousMethod__2(object s, System.EventArgs e) Line 253
[External Code]
MY.exe!MY.Main()
- What does is waiting
SetVisibleCore()
? - What happen on the native
code? Shall it be affected by
GarbageCollector()
? Task
forasync/await
may hit on theUiThread
?