dotnet / winforms

Windows Forms is a .NET UI framework for building Windows desktop applications.
MIT License
4.35k stars 965 forks source link

Disabled tests: WindowsFormsSynchronizationContext #3297

Open weltkante opened 4 years ago

weltkante commented 4 years ago

.NET Core Version: master branch

Have you experienced this same bug with .NET Framework?: no

Problem description:

Following three tests are blocking the update of xunit.stafact (#3122) and will be skipped by that PR unless they get fixed before the package update PR gets merged.

They are a WinFormsFact/Theory and dispose the SynchronizationContext early. This leads to either crashing the whole process in debug builds due to a failed assert, or failing tests in release builds. Reason is that WinFormsFact/Theory after the package update will pump messages via DoEvents, calling this after the SC gets disposed will lead to an exception (or crash in debug build).

(There may also be the question if this being an assert is the correct choice, maybe its worth checking explicitly for the current thread being a disposed thread, to give a useful exception instead. Or maybe its time to get rid of the public Dispose on WindowsFormsSynchronizationContext, it feels inappropriate since the WFSC is not the owner of the underlying infrastructure, there can be any number of WFSC and disposing one is terminating the infrastructure for all of them, thats not how the disposable pattern is supposed to work.)

The error gets blamed onto stafact infrastructure, but the tests are causing it by disposing early. Failed CI build can be found here: https://github.com/dotnet/winforms/runs/681811749

Debug build:

Unhandled exception. System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
   at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Control.cs:line 6896
   at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Control.cs:line 4707
   at System.Windows.Forms.WindowsFormsSynchronizationContext.Post(SendOrPostCallback d, Object state) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 90
   at Xunit.Sdk.Utilities.SyncContextAwaiter.OnCompleted(Action continuation)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AwaitUnsafeOnCompleted[TAwaiter](TAwaiter& awaiter, IAsyncStateMachineBox box)
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_1(Object state)
   at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
    System.Windows.Forms.Tests.ApplicationTests.Application_EnableVisualStyles_InvokeAfterGettingRenderWithVisualStyles_Success [SKIP]
      Crash with AbandonedMutexException. See: https://github.com/dotnet/arcade/issues/5325
Process terminated. Assertion Failed
Marshaling control should have created its handle in its ctor.
   at System.Windows.Forms.WindowsFormsSynchronizationContext..ctor() in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 35
   at System.Windows.Forms.WindowsFormsSynchronizationContext.InstallIfNeeded() in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 143
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Application.ThreadContext.cs:line 1119
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Application.ThreadContext.cs:line 992
   at System.Windows.Forms.Application.DoEvents() in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Application.cs:line 811
   at Xunit.Sdk.WinFormsSynchronizationContextAdapter.PumpTill(SynchronizationContext synchronizationContext, Task task)
   at Xunit.Sdk.ThreadRental.<>c__DisplayClass11_0.<CreateAsync>b__0()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Release build:

Unhandled exception. System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
   at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs:line 6896
   at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs:line 4706
   at System.Windows.Forms.WindowsFormsSynchronizationContext.Post(SendOrPostCallback d, Object state) in /_/src/System.Windows.Forms/src/System/Windows/Forms/WindowsFormsSynchronizationContext.cs:line 91
   at Xunit.Sdk.Utilities.SyncContextAwaiter.OnCompleted(Action continuation)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AwaitUnsafeOnCompleted[TAwaiter](TAwaiter& awaiter, IAsyncStateMachineBox box)
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_1(Object state)
   at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

Expected behavior: Tests using WinFormsFact/Theory should not dispose the infrastructure they are running on

Minimal repro: Execute one of those tests after updating xunit.stafact to 1.0.33-beta or newer

/cc: @hughbe @RussKie

AArnott commented 4 years ago

Thanks for filing. FWIW, if there's pushback on this, we can investigate making xunit.stafact more resilient to the SynchronizationContext being disposed.

weltkante commented 4 years ago

I don't think theres much you can do on the stafact side. There is no guarantee that the test is done at the point where the SC is disposed and just stopping to pump messages can lead to deadlocks.

Also for any workaround in stafact there is the whole issue of determining if the SC (or rather, the underlying infrastructure) is disposed, the unit test has its own instance of WFSC but its sharing the same WinForms infrastructure with xunit.stafact so disposing either SC will make the other inoperable. (Thats why making WFSC disposable is bad API design, its not the owner of the underlying infrastructure.)

The right thing to do is not disposing the infrastructure you're running on during a test. As far as the test is concerned there is no strong reason for these to be WinFormsFact/Theory, but when I tried turning them into StaFact/Theory that just makes the tests explode differently (not in xunit.stafact but in the test itself). I don't know exactly why that wasn't a problem before, but it indicates that the way the tests are written is wrong.

Stack trace in debug build with offending tests turned into StaFact/Theory ``` Process terminated. Assertion Failed Marshaling control should have created its handle in its ctor. at System.Windows.Forms.WindowsFormsSynchronizationContext..ctor() in D:\projects\winforms\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 37 at System.Windows.Forms.Tests.WindowsFormsSynchronizationContextTests.WindowsFormsSynchronizationContext_Post_InvokeDisposed_Nop(Object state) in D:\projects\winforms\src\System.Windows.Forms\tests\UnitTests\System\Windows\Forms\WindowsFormsSynchronizationContextTests.cs:line 142 at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) at Xunit.Sdk.TestInvoker`1.CallTestMethod(Object testClassInstance) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestInvoker.cs:line 150 at Xunit.Sdk.UITestInvoker.<>c__DisplayClass2_0.<b__2>d.MoveNext() at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.UITestInvoker.<>c__DisplayClass2_0.b__2() at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:line 48 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) at Xunit.Sdk.UITestInvoker.b__2_0() at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.UITestInvoker.b__2_0() at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 90 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) at Xunit.Sdk.UITestInvoker.RunAsync() at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.UITestInvoker.RunAsync() at Xunit.Sdk.UITestRunner.InvokeTestMethodAsync(ExceptionAggregator aggregator) at Xunit.Sdk.XunitTestRunner.InvokeTestAsync(ExceptionAggregator aggregator) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\XunitTestRunner.cs:line 67 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.XunitTestRunner.InvokeTestAsync(ExceptionAggregator aggregator) at Xunit.Sdk.TestRunner`1.<>c__DisplayClass43_0.b__0() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestRunner.cs:line 149 at Xunit.Sdk.ExceptionAggregator.RunAsync[T](Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 107 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.ExceptionAggregator.RunAsync[T](Func`1 code) at Xunit.Sdk.TestRunner`1.RunAsync() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestRunner.cs:line 149 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.TestRunner`1.RunAsync() at Xunit.Sdk.XunitTheoryTestCaseRunner.RunTestAsync() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\XunitTheoryTestCaseRunner.cs:line 144 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.XunitTheoryTestCaseRunner.RunTestAsync() at Xunit.Sdk.TestCaseRunner`1.RunAsync() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestCaseRunner.cs:line 82 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Xunit.Sdk.TestCaseRunner`1.RunAsync() at Xunit.Sdk.UITheoryTestCase.RunAsync(IMessageSink diagnosticMessageSink, IMessageBus messageBus, Object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object s) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext() at Xunit.Sdk.Utilities.SyncContextAwaiter.<>c.b__5_0(Object s) at Xunit.Sdk.UISynchronizationContext.TryOneWorkItem() at Xunit.Sdk.UISynchronizationContext.PumpMessages(Task untilCompleted) at Xunit.Sdk.UISynchronizationContext.Adapter.PumpTill(SynchronizationContext syncContext, Task task) at Xunit.Sdk.ThreadRental.<>c__DisplayClass11_0.b__0() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() ```
RussKie commented 4 years ago

Not sure if it is related, but just observed a failure in https://dev.azure.com/dnceng/public/_build/results?buildId=677815&view=results

XUnit : error : Tests failed: F:\workspace\_work\1\s\artifacts\log\Release\System.Windows.Forms.Tests_netcoreapp5.0_x86.log [netcoreapp5.0|x86] [F:\workspace\_work\1\s\src\System.Windows.Forms\tests\UnitTests\System.Windows.Forms.Tests.csproj]

Very little information unfortunately:

Unhandled exception. System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
   at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs:line 6870
   at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs:line 4685
   at System.Windows.Forms.WindowsFormsSynchronizationContext.Post(SendOrPostCallback d, Object state) in /_/src/System.Windows.Forms/src/System/Windows/Forms/WindowsFormsSynchronizationContext.cs:line 91
   at Xunit.Sdk.Utilities.SyncContextAwaiter.OnCompleted(Action continuation)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AwaitUnsafeOnCompleted[TAwaiter](TAwaiter& awaiter, IAsyncStateMachineBox box)
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__140_1(Object state)
   at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
weltkante commented 4 years ago

Yeah I'm getting that locally as well from time to time, stack trace looks like its the same root cause (disposed WFSC) but probably not doing it intentionally because then it would fail reliably. For what its worth I actually captured this one in a full dump while trying to repro the other random crashes.

I don't know if its possible to read off the dump where the exception is coming from, the exception seems to be rethrown in the thread pool, meaning the managed debugger loses any connection to the failed task. Maybe someone from the runtime team can take a look and tell if its possible to recover any information about the failing task/thread (task my refer to variables identifying the test; thread name contains the test name if this was thrown on a test thread)

RussKie commented 4 years ago

Related https://github.com/dotnet/winforms/issues/3358#issuecomment-641705284

RussKie commented 4 years ago

Observed in https://dev.azure.com/dnceng/public/_build/results?buildId=726290&view=results for https://github.com/dotnet/winforms/pull/3563 🤔 Likely a red herring...

Stack trace ``` Unhandled exception. System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created. at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Control.cs:line 6779 at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Control.cs:line 4674 at System.Windows.Forms.WindowsFormsSynchronizationContext.Post(SendOrPostCallback d, Object state) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 90 at Xunit.Sdk.Utilities.SyncContextAwaiter.OnCompleted(Action continuation) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AwaitUnsafeOnCompleted[TAwaiter](TAwaiter& awaiter, IAsyncStateMachineBox box) --- End of stack trace from previous location --- at System.Threading.Tasks.Task.<>c.b__140_1(Object state) at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Process terminated. Assertion Failed Marshaling control should have created its handle in its ctor. at System.Windows.Forms.WindowsFormsSynchronizationContext..ctor() in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 35 at System.Windows.Forms.WindowsFormsSynchronizationContext.InstallIfNeeded() in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\WindowsFormsSynchronizationContext.cs:line 143 at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Application.ThreadContext.cs:line 1117 at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context) in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Application.ThreadContext.cs:line 992 at System.Windows.Forms.Application.DoEvents() in F:\workspace\_work\1\s\src\System.Windows.Forms\src\System\Windows\Forms\Application.cs:line 811 at Xunit.Sdk.WinFormsSynchronizationContextAdapter.PumpTill(SynchronizationContext synchronizationContext, Task task) at Xunit.Sdk.ThreadRental.<>c__DisplayClass11_0.b__0() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() ```
Tanya-Solyanik commented 1 month ago

@LeafShi1 - please see if you are already tracking this test failure, if you have another bug, please link it here and close this one.

LeafShi1 commented 1 month ago

@LeafShi1 - please see if you are already tracking this test failure, if you have another bug, please link it here and close this one.

@Tanya-Solyanik This is a fixed error. We have not created other issue to track them. Even if #3122 has been merged, the three cases are still fail.