0

I call aynchronious methods and something deadlocks. Now I don't have any idea why this is happening. I don't think that this is a standard deadlock, because I call Task.Result in Main method - NOT IN UI thread. What's more, I also tried using async Main without any calls to Task. But the result is the same.

Problem is with WinForms project. Everything starts in Main method:

bool res = Task.Run(() => contr.RegisterPremiseAllInOne()).Result;

This is imple call to asynchronious method RegisterPremiseAllInOne in new Task.

What happens next... RegisterPremiseAllInOne works more-like like that (simplified model):

public async Task<bool> RegisterPremiseAllInOne()
{
   AppUser loggedAppUser = await appUserContr.GetLoggedAppUser(); // <-- everything works fine here
   if(loggedAppUser == null)
      return false;

   var premises = await GetPremisesForLoggedUser(); //at the end there is a deadlock
}

I will show you now what calls does each and every method do (everything ends up with rest queries to WebApi):

GetLoggedAppUser -> 
await API.Users.GetLoggedAppUser() -> 
await ClientHelper.GetObjectFromRequest<Appuser>("AppUsers/logged") -> 
await SendAsyncRequest() -> 
await HttpClient.SendAsync()

I hope this is quite readable.

This path works well. What's interesting, GetPremisesForLoggedUser is partially convergent with GetLoggedAppUser and looks like that:

GetPremisesForLoggedUser -> 
await API.Premises.GetForLoggedUser() -> 
await ClientHelper.GetListFromRequest<premise>("premises/logged") -> 
await SendAsyncRequest() -> 
await HttpClient.SendAsync()

As you can see there is a straight path. Call to API and eventually sending a http request.

Deadlock is within SendAsync from HttpClient. I don't have any idea why. Yet the first path works fine.

On the server side everything fine as well. Server returns successful response. But the client hangs.

I think it should work. I don't mix synchronious with async code. All the methods return Task<T> and are marked as async.

Maybe someone know where the problem may exists? Maybe you will want to see some screenshots from debug windows: Parallel Stack / Tasks?

Adam Jachocki
  • 1,897
  • 1
  • 12
  • 28
  • 3
    Don't use `.Result` or `.Wait` on tasks unless you know **explicitly** what you're doing. If your first line of code is from the program main code, with newer C# versions you can make this method also async and use await instead. – Lasse V. Karlsen May 24 '18 at 10:01
  • This very much is the standard deadlock issue. Your blocking (`.Result`) on an async call (`Task.Run(() => contr.RegisterPremiseAllInOne())`) – Liam May 24 '18 at 10:04
  • Possible duplicate of [Why does this async action hang?](https://stackoverflow.com/questions/14526377/why-does-this-async-action-hang) – Liam May 24 '18 at 10:04
  • Well, I tried with async Main without Task.Result and the outcome is the same. What's more I don't call Result in UI thread so it should work (and it works several lines earlier with different call) – Adam Jachocki May 24 '18 at 10:13
  • Does *not* look like standard deadlock to me. Result by itself is not dangerous. The standard deadlock arises because the body of the task tries to enter into a sync context that is in use. Task.Run runs without sync context. That pattern is generally safe and normally a recommended workaround for the standard deadlock if a true fix is not possible. – usr May 24 '18 at 10:27
  • `Deadlock is within SendAsync` as a test you could wrap that call in `await Task.Run(() => SendAsync(...))`. You also could insert assertions into your code that make sure the current sync context is null where you expect it to be null. Also, you can start adding ConfigureAwait(false) and see if the deadlock goes away. – usr May 24 '18 at 10:30
  • So you are doing this before your winform application starts? – Evk May 24 '18 at 10:38
  • Yes, exactly. Just before Application.Run. Although I am creating, showing and disposing some form earlier. But I don't think that this is a clue. – Adam Jachocki May 24 '18 at 10:48
  • That's interesting... When I do: `HttpResponseMessage response = await Task.Run(() => client.SendAsync(rm));` instead of just `await client.SendAsync()` it also hangs :| – Adam Jachocki May 24 '18 at 10:55
  • Then check value of `SynchronizationContext.Current` right before you are calling `SendAsync`. – Evk May 24 '18 at 11:45

1 Answers1

1

OK, thanks to @usr and @Evk I found the bug. It was however standard UI deadlock but little more camouflaged.

Just before I was calling GetPremisesForLoggedUser() I was CREATING a form using factory (IoC Container):

IPremisePickerView view = objFactory.Resolve<IPremisePickerView>();
var premises = await GetPremisesForLoggedUser();

When a form is created the thread that this form belongs to automatiocally becomes UI thread. This is done in Form class I think.

So it turns out that I was calling await GetPremisesForLoggedUser()

on UI thread. So probably all the task started to run on UI thread (from some point). But does it mean that Task.Run.Result called in Main was blocking UI thread waiting for result? I'm not sure, becase this was called on main thread (in this case NOT UI thread). So if someone could make up my answer it would be nice.

Anyway, after moving form creation below the call to async method everything started working.

Adam Jachocki
  • 1,897
  • 1
  • 12
  • 28
  • Good that you found the bug. It seems worth noting that systematic testing using Task.Run and SyncContext.Current assertions was the key to finding it. Debugging should be less staring at the screen and more investigating and experimenting sometimes. – usr May 26 '18 at 10:03