BennoDual
BennoDual

Reputation: 6259

Deadlock when calling Dispatcher.Invoke() twice

I have two dispatcher:

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 will run in a deadlock.

the call of

dispatcher1.Invoke()

is now waiting in

DispatcherSynchronizationContext.Wait()

and also the dispatcher2 after calling

dispatcher2.Invoke()

is waiting in

DispatcherSynchronizationContext.Wait

I cannot change the Invoke-Calls to async calls (BeginInvoke) because I need the result.

This was not the case with .NET 4.0 - only since I have changed to .NET 4.5.

Is there a way to solve this?

Upvotes: 3

Views: 1498

Answers (1)

Mike Strobel
Mike Strobel

Reputation: 25623

This is a really, really terrible solution, and you should not use it. Ever.

But if you feel like living dangerously, try replacing your Invoke calls with the InvokeAndPump extension method below. (Don't replace every call to Invoke in your project--just the problematic ones 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 help anyone who uses this and expects it to work reliably. I'm only posting it out of curiosity and because I am evil. Mostly the evil.

Upvotes: 4

Related Questions