Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Console application using async/await stops receiving after sending #3895

Closed
markrendle opened this issue Mar 22, 2017 · 4 comments
Closed
Assignees
Milestone

Comments

@markrendle
Copy link

markrendle commented Mar 22, 2017

I was working on a spike using a pair of console applications to send and receive messages, and hit a bit of odd behavior. In the subscriber application, once it had invoked a method on the Hub using an await, it stopped receiving new messages. Initially this meant it never received messages because it called a Subscribe method right away, but digging into it I found that it would receive quite happily until it called a method.

When the method is invoked with a .Wait() instead of await everything is fine.

I'm not sure if this is to do with the way threads are handled in console applications (as opposed to WPF applications which have a SynchronizationContext), or is down to a problem in the C# SignalR client.

Expected behavior

Would expect to be able to await hubProxy.Invoke("Foo"); and continue to receive messages.

Actual behavior

Using await hubProxy.Invoke("Foo"); breaks the connection somehow.

Steps to reproduce

class Program
{
  static void Main()
  {
    Run().Wait();
  }

  static async Task Run()
  {
    Console.WriteLine("[Subscriber] press Enter to enter...");
    Console.ReadLine();

    var hubConnection = new HubConnection("http://localhost:39103/");
    var hubProxy = hubConnection.CreateHubProxy("MyHub");
    hubProxy.On<string>("OnMessage", msg =>
    {
      Console.WriteLine($"Received: {msg}");
    });

    await hubConnection.Start();

    // Receiving messages quite happily at this point

    Console.WriteLine("And press Enter again to break the application...");
    Console.ReadLine();

    await hubProxy.Invoke("Foo", "Bar");

    // NO MESSAGES FOR YOU!

    Console.WriteLine("And press Enter to Exit, because computers...");
    Console.ReadLine();
  }
}
@markrendle
Copy link
Author

I should point out, the reason I'm concerned about this even though it's just a spike is that the actual service I'm working on will be consumed from ASP.NET Web API services as well as WPF desktop applications.

@moozzyk
Copy link
Contributor

moozzyk commented Mar 22, 2017

It's a very implicit deadlock. If you turn on tracing you will see that the messages are still being received by the client only your callback is not being invoked because the previous one has not finished yet:

And press Enter to Exit, because computers...
16:44:27.6966075 - b8022975-3d42-4935-952d-7c43a392d049 - WS: OnMessage({"C":"d-59F20992-B,18|S,0|T,1","M":[{"H":"ChatHub","M":"broadcastMessage","A":["adfasf","sadf"]}]})
16:44:28.6936798 - b8022975-3d42-4935-952d-7c43a392d049 - WS: OnMessage({})
16:44:31.4080330 - b8022975-3d42-4935-952d-7c43a392d049 - WS: OnMessage({"C":"d-59F20992-B,19|S,0|T,1","M":[{"H":"ChatHub","M":"broadcastMessage","A":["adfasf","adfsa"]}]})
16:44:36.2056851 - b8022975-3d42-4935-952d-7c43a392d049 - OnError(Microsoft.AspNet.SignalR.Client.Infrastructure.SlowCallbackException: Possible deadlock detected. A callback registered with "HubProxy.On" or "Connection.Received" has been executing for at least 10 seconds.)
16:44:38.6935113 - b8022975-3d42-4935-952d-7c43a392d049 - WS: OnMessage({})

On your side you could fix this by changing Console.ReadLine(); to await Console.In.ReadLineAsync() but I believe that the root cause here is that by default user code runs on the same thread as the task completed with TaskCompletionSource.SetResult(). The client should dispatch setting result to a different thread or use the TaskCreationOption.RunContinuationsAsynchronously when creating the TCS.

@BowserKingKoopa
Copy link

This thread is my async await experience in a nutshell.

@moozzyk moozzyk self-assigned this Mar 22, 2017
@moozzyk moozzyk added this to the 2.2.2 milestone Mar 22, 2017
moozzyk pushed a commit that referenced this issue Mar 24, 2017
moozzyk pushed a commit that referenced this issue Mar 30, 2017
moozzyk pushed a commit that referenced this issue Mar 30, 2017
Fixing an issue where user's code would run a continuation of `HubProxy.Invoke` on a SignalR thread because we were using TaskCompletionSource with default settings. If the user's code blocked it would completely block the receive queue and the user will no longer get any notifications even though the client processes them and add to the queue.  The fix is to dispatch the completion of `HubProxy.Invoke` to a different thread (note that we can't use `TaskCreationOptions.RunContinuationsAsynchronously` because some platforms we compile for do not support this).
moozzyk pushed a commit that referenced this issue Mar 31, 2017
Fixing an issue where user's code would run a continuation of `HubProxy.Invoke` on a SignalR thread because we were using TaskCompletionSource with default settings. If the user's code blocked it would completely block the receive queue and the user will no longer get any notifications even though the client processes them and add to the queue.  The fix is to dispatch the completion of `HubProxy.Invoke` to a different thread (note that we can't use `TaskCreationOptions.RunContinuationsAsynchronously` because some platforms we compile for do not support this).
moozzyk pushed a commit that referenced this issue Mar 31, 2017
Fixing an issue where user's code would run a continuation of `HubProxy.Invoke` on a SignalR thread because we were using TaskCompletionSource with default settings. If the user's code blocked it would completely block the receive queue and the user will no longer get any notifications even though the client processes them and add to the queue.  The fix is to dispatch the completion of `HubProxy.Invoke` to a different thread (note that we can't use `TaskCreationOptions.RunContinuationsAsynchronously` because some platforms we compile for do not support this).
@moozzyk
Copy link
Contributor

moozzyk commented Apr 3, 2017

Fixed in e55b89a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants