Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronously calling async method in WebAPI deadlocks even with ConfigureAwait(false)

I have the nuget package Esri.ArcGISRuntime and I need to call the method QueryTask.ExecuteAsync in one of my Web API 2 controllers. There is no synchronous counter part so in my library c# code I am using a wrapper like

    private QueryResult ExecuteSync()
    {
        var queryResults = ExecuteAsync();
        queryResults.Wait();
        return queryResults.Result;
    }

    private async Task<QueryResult> ExecuteQueryTaskAsync()
    {
        var queryTask = new QueryTask(_uri);
        return await queryTask.ExecuteAsync(_query).ConfigureAwait(false);
    }

Which works perfectly in my program/service. But using ExecuteSync this way in a Web API 2 controller causes it to completely freeze up and never return a response.

I have done some research and believe the culprit is mentioned here: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

I absolutely do not want to use the function asynchronously. The above functions are so core and hidden deep within like 4 wrappers, that it would be a major overhaul of my library class to bubble up async methods just to support this one web api call.

I am looking for work-arounds/hacks/suggestions around this weird behavior of web API to allow me to run this async method synchronously and not have it deadlock

like image 692
ParoX Avatar asked Feb 04 '17 02:02

ParoX


People also ask

Should you always use ConfigureAwait false?

As a general rule, every piece of code that is not in a view model and/or that does not need to go back on the main thread should use ConfigureAwait false. This is simple, easy and can improve the performance of an application by freeing the UI thread for a little longer.

Why is ConfigureAwait false?

ConfigureAwait(continueOnCapturedContext: false) is used to avoid forcing the callback to be invoked on the original context or scheduler. This has a few benefits: Improving performance.

When would you not use ConfigureAwait false?

If the await task. ConfigureAwait(false) involves a task that's already completed by the time it's awaited (which is actually incredibly common), then the ConfigureAwait(false) will be meaningless, as the thread continues to execute code in the method after this and still in the same context that was there previously.

What is ConfigureAwait () used for?

In the code that relies on the asynchronous programming model ( async / await keywords), ConfigureAwait() calls are often used to manage the synchronization context. The way ConfigureAwait() calls work and their usage scenarios are explained in detail in this Microsoft .


1 Answers

I absolutely do not want to use the function asynchronously.

I have to say it, though. Asynchronous code is the best solution. The operation you're doing is asynchronous, and exposing a synchronous API for it is problematic at best.

Does it take time? Sure. But your code will be better off for it.

I am looking for work-arounds/hacks/suggestions

I have an entire article on the subject of brownfield async development, where I cover all the known hacks along with their drawbacks.

In your particular case (calling from WebApi on non-Core ASP.NET and considering that it does work from a Console/Win32Service-style app), I'd say the Thread Pool Hack should work for you. It looks like this:

private QueryResult ExecuteSync()
{
  return Task.Run(() => ExecuteAsync()).GetAwaiter().GetResult();
}

The idea is that ExecuteAsync is run on a thread pool thread, outside of the request context. The request thread is then blocked until the asynchronous work completes.

like image 105
Stephen Cleary Avatar answered Sep 21 '22 10:09

Stephen Cleary