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

awaiting a HubProxy.Invoke method makes the HubConnection.Dispose take 30 seconds to dispose #2653

Closed
maboivin opened this issue Oct 22, 2013 · 5 comments
Assignees
Milestone

Comments

@maboivin
Copy link

I have a simple Hub in a an ASP.NET MVC5 web app. I'm using SignalR 2.0.0.

public class MyHub : Hub
{
    public void SendMessage(string message)
    {
        this.Clients.All.OnMessage(message);
    }
}

I have a simple client in .NET 4.5.

class Program
{
    static void Main(string[] args)
    {
        Run();

        Console.ReadKey();
    }

    static async void Run()
    {
        var url = "http://localhost:65479/";

        var tcs = new TaskCompletionSource<bool>();
        var sw = new Stopwatch();

        using (var hubConnection = new HubConnection(url))
        {
            var hubProxy = hubConnection.CreateHubProxy("MyHub");
            await hubConnection.Start();

            hubProxy.On<string>(
                "OnMessage",
                 message =>
                 {
                     Console.WriteLine(message);

                     tcs.SetResult(true);
                 });

            // When awaiting this call, it takes 30 seconds to dispose the hub connection.
            /*await*/ hubProxy.Invoke("SendMessage", "my message");

            // EDITED: Replaced tcs.Task.Wait(30 * 1000); with the following:
            await tcs.Task;

            sw.Start();
        }

        sw.Stop();

        // It takes 30 seconds to dispose...
        Console.WriteLine(sw.Elapsed);
    }
}

My issue is that when I await the call to hubProxy.Invoke(), the hub connection takes 30 seconds to dispose (which is the default timeout). But if I don't await the same call, it disposes fine. I have a simple project if needed to repro.

What should I do to dispose the connection without having to wait 30 seconds? Is it a bug or is my code wrong?

@davidfowl
Copy link
Member

Try doing:

await tcs.Task

It's bad to mix sync and async code.

@maboivin
Copy link
Author

Sorry, bad copy/paste. I previously tried it with .NET 4.0 and it was working fine without async calls. The original code was like you said "await tcs.Task" and it doesn't change anything, it still takes 30 seconds to dispose. I also tried the following code:

await tcs.Task.TimeoutAfter(30 * 1000);

and

static class TaskExtensions
{
    public static async Task<T> TimeoutAfter<T>(this Task<T> task, int timeoutInMillis)
    {
        using (var cancellationTokenSource = new CancellationTokenSource())
        {
            if (task == await Task.WhenAny(task, Task.Delay(timeoutInMillis, cancellationTokenSource.Token)))
            {
                cancellationTokenSource.Cancel();

                return await task;
            }
            else
            {
                throw new TimeoutException();
            }
        }
    }
}

@davidfowl
Copy link
Member

By default calling dispose/stop can take up to 30 seconds as it tries to abort the connection gracefully. For some reason that is hanging and we can look into why. I assume you're using 2.0.0 rtw?

@maboivin
Copy link
Author

You're right. I'm using 2.0.0 rtw. If you want me to upload a repro solution, let me know.

@ghost ghost assigned halter73 and DamianEdwards Oct 23, 2013
DamianEdwards added a commit that referenced this issue Oct 23, 2013
- This fixes the issue with awaiting an Invoke call and then calling Stop()
- #2653
DamianEdwards added a commit that referenced this issue Oct 25, 2013
- This ensures that calling stop from a message receive doesn't deadlock the receive loop
- Using TaskQueue.cs so added that to client projects
- Had to modify TaskQueue to work in Portable, now do optimistic increment then unwind if we exceed the max size
- Added a test to verify that async connection start, invoke, stop works without dead-locking
- #2653
DamianEdwards added a commit that referenced this issue Nov 1, 2013
- This ensures that calling stop from a message receive doesn't deadlock the receive loop
- Using TaskQueue.cs so added that to client projects
- Had to modify TaskQueue to work in Portable, now do optimistic increment then unwind if we exceed the max size
- Added a test to verify that async connection start, invoke, stop works without dead-locking
- #2653
DamianEdwards added a commit that referenced this issue Nov 1, 2013
- Drain the receive queue when Stop called
- Raise connection error event if queued OnReceived callback throws
- Remove obsolete buffered messages tests
- #2653
DamianEdwards added a commit that referenced this issue Nov 1, 2013
DamianEdwards added a commit that referenced this issue Nov 1, 2013
DamianEdwards added a commit that referenced this issue Nov 1, 2013
- Will log a separate issue to track the reason why this causes the failure
- #2653
DamianEdwards added a commit that referenced this issue Nov 4, 2013
- This ensures that calling stop from a message receive doesn't deadlock the receive loop
- Using TaskQueue.cs so added that to client projects
- Had to modify TaskQueue to work in Portable, now do optimistic increment then unwind if we exceed the max size
- Added a test to verify that async connection start, invoke, stop works without dead-locking
- #2653
DamianEdwards added a commit that referenced this issue Nov 4, 2013
- Drain the receive queue when Stop called
- Raise connection error event if queued OnReceived callback throws
- Remove obsolete buffered messages tests
- #2653
DamianEdwards added a commit that referenced this issue Nov 4, 2013
DamianEdwards added a commit that referenced this issue Nov 4, 2013
DamianEdwards added a commit that referenced this issue Nov 4, 2013
- Will log a separate issue to track the reason why this causes the failure
- #2653
DamianEdwards added a commit that referenced this issue Nov 4, 2013
DamianEdwards added a commit that referenced this issue Nov 4, 2013
- Tests that received messages are not processed until the start init message is received and that the execution order is correct
- #2653
DamianEdwards added a commit that referenced this issue Nov 4, 2013
DamianEdwards added a commit that referenced this issue Nov 4, 2013
- This ensures that calling stop from a message receive doesn't deadlock the receive loop
- Using TaskQueue.cs so added that to client projects
- Had to modify TaskQueue to work in Portable, now do optimistic increment then unwind if we exceed the max size
- Added a test to verify that async connection start, invoke, stop works without dead-locking
- Drain the receive queue when Stop called
- Raise connection error event if queued OnReceived callback throws
- Removed duplicate call to start from failing test (Will log a separate issue to track the reason why this causes the failure)
- #2653
@ghost ghost assigned gustavo-armenta Nov 4, 2013
DamianEdwards added a commit that referenced this issue Nov 4, 2013
DamianEdwards added a commit that referenced this issue Nov 4, 2013
- This preserves the previous client behavior
- #2653
DamianEdwards added a commit that referenced this issue Nov 4, 2013
@gustavo-armenta
Copy link
Contributor

tested repro and now connection disposes in less than one second

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

5 participants