Skip to content

Cross thread Sync context stealing #276

@TomKuhn

Description

@TomKuhn

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions