3

Suppose I'm writing a custom MVC filter which does some asynchronous calls within the method overrides, like so:

public class MyActionFilter : System.Web.Mvc.ActionFilterAttribute
{
  public override void OnActionExecuted(ActionExecutedContext ctx)
  {
    var stuff = ConfigureAwaitHelper1().Result;
    // do stuff
  }

  public override void OnActionExecuting(ActionExecutingContext ctx)
  {
    var stuff = ConfigureAwaitHelper2().Result;
    // do stuff
  }

  private async Task<string> ConfigureAwaitHelper1()
  {
    var result = await client.GetAsStringAsync("blah.com").ConfigureAwait(false);
    return result;
  }

  private async Task<string> ConfigureAwaitHelper2()
  {
    return await client.GetAsStringAsync("blah.com").ConfigureAwait(false);
  }
}

Why does OnActionExecuting deadlock, whereas OnActionExecuted does not? I don't see the fundamental difference between the two. The act of returning happens only after the asynchronous task is complete, which is rather like putting the result into an "anonymous return" local var before returning it, so I don't see why the former should deadlock.

gzak
  • 3,908
  • 6
  • 33
  • 56
  • 5
    You are asking "When I break the rules one way it does not work but when I break the rules another way it does work, why am I allowed to break the rules the 2nd way", the answer is you are not allowed to break the rules the 2nd way, you just got lucky that you didn't get caught breaking the rules. I don't know why the 2nd one works, but to me it does not matter because you shouldn't be blocking it anyway. – Scott Chamberlain Jul 18 '16 at 21:40
  • 2
    Also, for `.ConfigureAwait(false)` to actually let you do `.Result` without deadlocking every single await down the call chain must have `.ConfigureAwait(false)` set on it. If a single one does not and that await is the one that caused the conintuation to happen you will get deadlocked. You need to show the code for `client.GetAsStringAsync` and every async/await call under it until you find the one that still has the context data. – Scott Chamberlain Jul 18 '16 at 21:43
  • What alternative do I have? `ActionFilterAttribute` is synchronous, and I have no control over the `client`. I'm aware of your concern of doing it all the way through, but let's just assume this sample is complete, and that there's nothing further to configure inside `client.GetAsStringAsync()`. – gzak Jul 18 '16 at 21:46
  • Can you double-check that it's `OnActionExecuting` the one deadlocking and not `OnActionExecuted`? – Diego Jul 18 '16 at 22:19
  • I just confirmed, both deadlock on my machine, but my colleague had some additional changes where she removed the async/await keywords from within the `client.GetAsStringAsync()` method and just returned the raw tasks directly. In her case, she saw the behavior as described here. – gzak Jul 18 '16 at 23:06
  • 1
    Possible duplicate of [Async Await Handler Deadlock](http://stackoverflow.com/questions/17859898/async-await-handler-deadlock) – Igor Jul 19 '16 at 12:26

1 Answers1

1

Why does OnActionExecuting deadlock, whereas OnActionExecuted does not?

I'm surprised it works at all. The reason you're experiencing the deadlock is due to the fact that you're invoking a .Result on a Task. This is evil and you should only ever invoke .Result and .Wait in console applications.

David Pine
  • 23,787
  • 10
  • 79
  • 107