-2

I wonder why exception handling behaves differently in asp.net and console for async/await calls. Consider this method:

        private static async Task Test()
        {
            try
            {
                await Task.Run(() =>
                {
                    throw new Exception("Test Exception");
                });
            }
            catch (Exception)
            {
                throw;
            }
        }

Catch does not hit in ASP.NET non-async controller like this working under IIS, neither in Test() nor in TestAction():

    [HttpGet]
    public virtual ActionResult TestAction()
    {
        try
        {
            Test().Wait();
        }
        catch (Exception)
        {
            throw;
        }
     }

Instead http request hangs. Same Console code or WindowsForms code work fine hitting catch in both places:

class Program
{   
    static void Main(string[] args)
    {           
        try
        {
            Test().Wait();
        }
        catch (Exception)
        {
            throw;
        }
    }
}

UPDATE:

This code work fine though (catch is called) in asp.net controller:

[HttpGet]
public virtual ActionResult TestAction()
{
        try
        {
            Task.Run(() =>
            {
                throw new Exception("Test Exception");
            }).Wait();
        }
        catch (Exception)
        {
            throw;
        }
  }
YMC
  • 4,925
  • 7
  • 53
  • 83
  • 1
    Deadlock? Instead of calling `.Wait()`, make `TestAction()` also `async` and `await` the method. That's generally the correct approach anyway, so might as well start there. – David Jan 17 '18 at 15:55
  • right, async Controller works fine if I put `await Test()` instead of `Test().Wait()` in an action method, but still want to know why it does not work for synchronous version – YMC Jan 17 '18 at 16:26
  • Have you tried adding `.ConfigureAwait(false)` to your `await Task.Run...` and seeing if it still happens? That's usually the first thing to try when running into a deadlock involving `async`/`await`. – kanders84152 Jan 17 '18 at 16:30
  • @YMC: This looks helpful: https://stackoverflow.com/questions/15021304/an-async-await-example-that-causes-a-deadlock – David Jan 17 '18 at 16:31
  • 2
    Using `.Wait()` on a task is something to be avoided unless you absolutely know what you're doing. See [Async/Await - Best Practices in Asynchronous Programming](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). – mason Jan 17 '18 at 16:38
  • @kanders84152, `await Task.Run(() => { throw new Exception("Test Exception"); }).ConfigureAwait(false);` does the trick and exception starts catching properly by calling context. Though I still do not totally understand why is a discrepancy here – YMC Jan 17 '18 at 16:50
  • 4
    @YMC `TestAction()` calls `Test()` which runs synchronously until `Task.Run...`. Then `await` relinquishes the current thread. The `Wait()` in the `TestAction()` now takes that thread and blocks it until the task finishes. Meanwhile, the task throws and is done. Control should return to the line after the `await`, but it can't, because it wants to return control to the thread that it was called from, which is blocked. Ergo, deadlock. `ConfigureAwait(false)` tells it that it is ok to return control on a different thread than the original one, thus allowing `TestAction()` to finish. – kanders84152 Jan 17 '18 at 16:57
  • @kanders84152 alright, now it makes sense for me, though not sure then how one-thread console and Windows application happened to work fine. Nevertheless, you're free to post it as an answer. Thanks! – YMC Jan 17 '18 at 17:01

1 Answers1

1

TestAction() calls Test() which runs synchronously until Task.Run.... Then await relinquishes the current thread. The Wait() in the TestAction() now takes that thread and blocks it until the task finishes. Meanwhile, the task throws and is done. Control should return to the line after the await, but it can't, because it wants to return control to the thread that it was called from, which is blocked. Ergo, deadlock. ConfigureAwait(false) tells it that it is ok to return control on a different thread than the original one, thus allowing TestAction() to finish.

It works fine in console applications because they don't have a SynchronizationContext that insists on returning control to the original thread. WPF and asp.net have that custom synchronization context because there are changes that have to be made from the calling thread.

kanders84152
  • 1,251
  • 10
  • 17