Deadlock when calling Dispatcher.Invoke () twice

I have two dispatchers:

dispatcher1
dispatcher2

      

Now when I call (this is a simplified sample of my complex code):

            var ret = dispatcher1.Invoke(() => {
                return dispatcher2.Invoke(() => {
                    return new object();
                });
            });

      

I'll run into a dead end.

call

dispatcher1.Invoke()

      

now waiting in

DispatcherSynchronizationContext.Wait()

      

and also dispatcher2 after the call

dispatcher2.Invoke()

      

waiting in

DispatcherSynchronizationContext.Wait

      

I am unable to change the callbacks for asynchronous calls (BeginInvoke) because I need the result.

This has not been the case since .NET 4.0 - only since I changed to .NET 4.5.

Is there a way to solve this problem?

+3


source to share


1 answer


This is a really scary solution, and you shouldn't use it. Ever.

But if you feel like your life is dangerous, try replacing your calls Invoke

with the extension method InvokeAndPump

below. (Don't replace every call with Invoke

in your project - just problematic in the question.)



public static class DispatcherExtensions
{
    public static T InvokeAndPump<T>(
        this Dispatcher dispatcher,
        Func<T> function,
        DispatcherPriority priority = DispatcherPriority.Normal)
    {
        if (dispatcher == null)
            throw new ArgumentNullException("dispatcher");
        if (function == null)
            throw new ArgumentNullException("function");

        // If you've found this code in your project, you are doomed.  <3

        Action wait, notify;

        var currentDispatcher = Dispatcher.FromThread(Thread.CurrentThread);
        if (currentDispatcher != null)
        {
            var frame = new DispatcherFrame();

            wait = () => Dispatcher.PushFrame(frame);
            notify = () => frame.Continue = false;
        }
        else
        {
            var waitEvent = new ManualResetEventSlim(false);

            wait = waitEvent.Wait;
            notify = waitEvent.Set;
        }

        var error = default(Exception);
        var result = default(T);

        dispatcher.BeginInvoke(
            priority,
            new Action(
                () =>
                {
                    try { result = function(); }
                    catch (Exception e) { error = e; }
                    finally { notify(); }
                }));

        // Hold on to your butts...

        wait();

        if (error != null)
            throw new TargetInvocationException(error);

        return result;
    }
}

      

Seriously, though: heaven is helping anyone who uses it and hopes it will work reliably. I'm only posting this out of curiosity and because I'm angry. Mostly evil.

+2


source







All Articles