Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# httpClient (block for async call) deadlock

Current Situation

There is a client that does a get-request by HttpClient.GetAsync. Unfortunately, for some reason, we need to block on that calls.

In order to do so, this Asynchelper class is used in order to avoid context-switch deadlocks (instead of just using .Result)

   public static class AsyncHelper
   {
       private static readonly TaskFactory _myTaskFactory = new
             TaskFactory(CancellationToken.None,
                         TaskCreationOptions.None,
                         TaskContinuationOptions.None,
                         TaskScheduler.Default);


    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

Then the actual call looks like this:

AsyncHelper.RunSync(Async Function() Await _httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead))

Problem

During a stress test using Netlimiter to reduce the network speed, we encountered a problem with requests that did not finish. For example, when I kill a network connection (in NetLimiter), such a request will stay forever on client side. It will stay in the AsyncHelper.RunSync-call until it runs into the httpClient.Timeout.

Shouldn't there be an exception that will end this call when the connection is lost? Am I missing something?

like image 467
DanielG Avatar asked Dec 06 '25 05:12

DanielG


1 Answers

The thing is that HTTP connections are usually riding over a TCP connection. So when you forcefully terminate a TCP connection without its being able to send its "I'm done here," (or FIN) packets, the other end of the connection is still happy to wait for more data, at least until some defined timeout which can be configured from above in socket-query operations.

One way to handle this is to set TCP Keep-Alive on the sockets. Note that this is very different from HTTP Keep-Alive. The other option is, as already seems to happen, to use a good-enough timeout.

like image 193
Yam Marcovic Avatar answered Dec 07 '25 20:12

Yam Marcovic