-
-
Notifications
You must be signed in to change notification settings - Fork 371
Description
I'm getting some crazy UI thread deadlocks after using AsyncEx.Context. WinForms app.
Here's the UI thread, doing UI things:
Application.Run(form);
Here's the sync context on this thread:
System.Windows.Forms.WindowsFormsSynchronizationContext, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 as expected.
However check this crazy callstack out:
System.dll!System.Collections.Concurrent.BlockingCollection<System.__Canon>.CheckDisposed() Line 1808 C#
System.dll!System.Collections.Concurrent.BlockingCollection<System.Tuple<System.Threading.Tasks.Task, bool>>.TryAddWithNoTimeValidation(System.Tuple<System.Threading.Tasks.Task, bool> item, int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 414 C#
Nito.AsyncEx.Context.dll!Nito.AsyncEx.AsyncContext.TaskQueue.TryAdd(System.Threading.Tasks.Task item, bool propagateExceptions) Line 57 C#
Nito.AsyncEx.Context.dll!Nito.AsyncEx.AsyncContext.Enqueue(System.Threading.Tasks.Task task, bool propagateExceptions) Line 91 C#
mscorlib.dll!System.Threading.Tasks.Task.ScheduleAndStart(bool needsProtection) Line 1946 C#
mscorlib.dll!System.Threading.Tasks.Task.InternalStartNew(System.Threading.Tasks.Task creatingTask, System.Delegate action, object state, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskScheduler scheduler, System.Threading.Tasks.TaskCreationOptions options, System.Threading.Tasks.InternalTaskOptions internalOptions, ref System.Threading.StackCrawlMark stackMark) Line 1294 C#
mscorlib.dll!System.Threading.Tasks.TaskFactory.StartNew(System.Action action, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskCreationOptions creationOptions, System.Threading.Tasks.TaskScheduler scheduler) Line 401 C#
Nito.AsyncEx.Tasks.dll!Nito.AsyncEx.TaskFactoryExtensions.Run(System.Threading.Tasks.TaskFactory this, System.Action action) Unknown
Nito.AsyncEx.Context.dll!Nito.AsyncEx.AsyncContext.AsyncContextSynchronizationContext.Send(System.Threading.SendOrPostCallback d, object state) Line 62 C#
System.dll!Microsoft.Win32.SystemEvents.SystemEventInvokeInfo.Invoke(bool checkFinalization, object[] args) Line 1634 C#
System.dll!Microsoft.Win32.SystemEvents.RaiseEvent(bool checkFinalization, object key, object[] args) Line 1314 C#
System.dll!Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(int msg, System.IntPtr wParam, System.IntPtr lParam) Line 1006 C#
System.dll!Microsoft.Win32.SystemEvents**.WindowProc**(System.IntPtr hWnd, int msg, System.IntPtr wParam, System.IntPtr lParam) Line 1491 C#
The UI thread has somehow issued a post/send to an AsyncEx context which I use on other worker threads, but NEVER the UI thread! That Sync Context getting posted to has long been disposed, because this callstack ends in an exception thrown by the _queue in TaskQueue.cs:
System.ObjectDisposedException
HResult=0x80131622
Message=The collection has been disposed.
Object name: 'BlockingCollection'.
Source=System
StackTrace:
at System.Collections.Concurrent.BlockingCollection`1.CheckDisposed() in f:\dd\NDP\fx\src\sys\system\collections\concurrent\BlockingCollection.cs:line 1810
It's very similar to this issue:
https://ikriv.com/dev/dotnet/MysteriousHang#WhatToDo
Which is also related to OnUserPreferenceChanged.
What I think is happening is that somehow a Control handle is being accidentally created on a non-UI thread, in which the Sync Context is AsyncEx's context. The control is auto-registering itself with SystemEvents to be notified when OnUserPreferenceChanged is raised. This registration also involves the current Sync Context, so it can post to it later.
The question is: How can I detect an erroneous handle creation on the non UI thread!