Skip to content

Add Dispatcher.Created static event #7301

Open
@matthew-a-thomas

Description

@matthew-a-thomas

The need

It's undesirable to instantiate a Dispatcher on a non-UI thread. Doing so is (usually) a bug and leads to several issues, such as:

  • Deadlock. Usually the dispatcher will not be running. But code may check if the current thread has a dispatcher and if so post a callback to it and wait to proceed until the callback has executed. Since the dispatcher is not running the callback never executes
  • Memory leak. Usually the dispatcher will not be running. But code may post callbacks to it. Those callbacks contain state which is strongly-referenced by the dispatcher. That state graph will not be garbage collected until the thread ends

However it is extremely easy to instantiate a Dispatcher on a non-UI thread. Broadly speaking there are two general ways this tends to happen:

  • Access Dispatcher.CurrentDispatcher
  • Instantiate anything that derives from DispatcherObject

It is usually possible for the disciplined solo programmer to avoid these issues in a small project. But sometimes people forget, and other times these issues are subtle and unexpected (e.g. I just recently learned that simply calling CommandManager.InvalidateRequerySuggested() creates a dispatcher). So these issues tend to crop up in larger projects with many developers. As a result it's virtually guaranteed that the software project will periodically encounter deadlocks, memory leaks, and other unexpected behavior.

A solution

I'd like to solve this problem by giving developers a proactive way to know when a dispatcher is instantiated. Developers can write #if DEBUG code that subscribes to the proposed event and inspects Thread.CurrentThread in the handler, and then log the stack trace when it's an undesirable thread. Then developers will know exactly where the offending code is and will be able to solve it promptly.

Alternatives

I think this would be better than the alternatives. I only know of a few alternatives (I'm open to hearing of more). In increasing order of hackiness:

  1. Use the Win32 API to be notified after a Win32 window is created. I assume such a hook would execute asynchronously, so this would really be no better than # 2, which is...
  2. Use reflection to poll Dispatcher._dispatchers (while under the Dispatcher._globalLock guard of course)
  3. Add a hook to intercept the call to the user32.dll > CreateWindowEx Win32 function call that happens while instantiating a dispatcher
  4. Edit the CLR method descriptor for Dispatcher.ctor or Dispatcher.get_CurrentDispatcher at runtime to point to my own hacked up method that does the same thing as this proposal but in a much messier way
  5. Run a fork of WPF

The proposal

public sealed class Dispatcher
{
  public static event DispatcherCreatedEventHandler? Created; // Add this event

  public static Dispatcher CurrentDispatcher
  {
    get
    {
      Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
      if(currentDispatcher == null)
      {
        currentDispatcher = new Dispatcher();

        //// The following line is new ////
        Created?.Invoke();
        //// The above line is new ////

      }
      return currentDispatcher;
    }
  }
}

// Add this delegate
public delegate void DispatcherCreatedEventHander();

Metadata

Metadata

Assignees

No one assigned

    Labels

    API suggestionEarly API idea and discussion, it is NOT ready for implementation

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions