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.
Would expect to be able to await hubProxy.Invoke("Foo");
and continue to receive messages.
Using await hubProxy.Invoke("Foo");
breaks the connection somehow.
```c#
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();
}
}
```
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.
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.
This thread is my async await experience in a nutshell.
Fixed in e55b89a45618d222e2c37a3b14969923c998899b
Most helpful comment
Fixed in e55b89a45618d222e2c37a3b14969923c998899b