Preventing a deadlock when calling an async method without using await
Asked Answered
G

3

14

I need to call a method returning a Task from within

public override void OnActionExecuting(ActionExecutingContext filterContext)

It wont let me make this method async it throws the following

An asynchronous module or handler completed while an asynchronous operation was still pending.

and when calling

 entityStorage.GetCurrentUser().Result

I get a deadlock. How can I avoid this?

I have been playing around with it coming up with stuff like

entityStorage.GetCurrentUser().Result.ConfigureAwait(false).GetAwaiter().GetResult();

But this isn't working. How can I do it? My solution will need to work with ASP.NET 4 and the Async Targetting Pack, I can't use ASP.NET 4.5 as am deploying to Azure.

Generality answered 9/8, 2012 at 16:32 Comment(3)
Found an answer here https://mcmap.net/q/54009/-how-would-i-run-an-async-task-lt-t-gt-method-synchronously if anyone has a better solution please shareGenerality
Are you deriving from AsyncController instead of Controller?Carrack
Please vote here for the MVC team to add support for async action filters.Carrack
I
2

Since await is just syntax sugar for the compiler rewriting a continuation for you, the most 'direct' path would be to take whatever code was going to follow your await and make it a ContinueWith call.

So, something like:

entityStorage.GetCurrentUser().ContinueWith(t =>
{
    // do your other stuff here
});
Ianteen answered 9/8, 2012 at 17:54 Comment(0)
C
13

The cause of the deadlock is explained here. In short, don't block on async code. You should use ConfigureAwait(false) in your library async code and await the results (not use Result or Wait).

Update: Please vote here for the MVC team to add support for async action filters.

Carrack answered 9/8, 2012 at 17:12 Comment(6)
I'm having a hard time find the link/page that says so, but I've had others tell me in the past that if providing a link (especially one external to SO) that you should copy the key bits into the SO answer. It's somewhat mentioned here, though, FWIW: meta.stackexchange.com/questions/118582/…Ianteen
You are correct; the meta question you're referring to was recently deleted as part of the "let's make SO a nicer place" strategy. However, as Jon Skeet commented: if I know the answer and only have time to post a link, it's better than nothing.Carrack
The problem I had is that if I tried to use the async keyword and make the method async then the method threw 'An asynchronous module or handler completed while an asynchronous operation was still pending.'Generality
ASP.NET is telling you that you can't use asynchronous methods there. You can kind of force it using ContinueWith, but that brings its own problems in ASP.NET.Carrack
OK, so is there a good solution to doing this without using .NET 4.5? The only other solution I found was the one I put as a comment to the question, which I actually ended up using as I needed to store a cookie after the call which wasn't possible when using ContinueWithGenerality
The core problem is that you aren't supposed to do asynchronous processing in OnActionExecuting. I think the cleanest way to force it is to use ConfigureAwait(false) inside GetCurrentUser and then use WaitAndUnwrapException to block on the Task returned by GetCurrentUser.Carrack
I
2

Since await is just syntax sugar for the compiler rewriting a continuation for you, the most 'direct' path would be to take whatever code was going to follow your await and make it a ContinueWith call.

So, something like:

entityStorage.GetCurrentUser().ContinueWith(t =>
{
    // do your other stuff here
});
Ianteen answered 9/8, 2012 at 17:54 Comment(0)
G
0

If you MUST convert asynch to synch.

public User GetCurrentUserSynch()
    {
        return Task.Run(() =>
        {
            var asyncResult = entityStorage.GetCurrentUser();
            while (!asyncResult.IsCompleted)
            {
                Application.Current.TryFindResource(new object()); // This is for WPF but you can do some other nonsense action of your choosing
            }

            return asyncResult.Result;
        }).Result;
    }

Otherwise use @Stephen's answer.

Gynaecocracy answered 29/9, 2014 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.