Get off the event loop before dispatch (#1952)

This commit is contained in:
David Fowler 2018-04-11 10:39:09 -07:00 committed by GitHub
parent 12b69a0fdb
commit cb5bba36fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 23 deletions

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Http.Connections.Internal
namespace Microsoft.AspNetCore.Internal
{
public static class AwaitableThreadPool
{

View File

@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Common\AwaitableThreadPool.cs" Link="AwaitableThreadPool.cs" />
<Compile Include="..\Common\MemoryBufferWriter.cs" Link="MemoryBufferWriter.cs" />
<Compile Include="..\Common\PipeWriterStream.cs" Link="PipeWriterStream.cs" />
<Compile Include="..\Common\WebSocketExtensions.cs" Link="WebSocketExtensions.cs" />

View File

@ -12,6 +12,7 @@ using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.DependencyInjection;
@ -433,6 +434,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
private async Task DispatchInvocationAsync(InvocationMessage invocation)
{
// Make sure we get off the main event loop before we dispatch into user code
await AwaitableThreadPool.Yield();
// Find the handler
if (!_handlers.TryGetValue(invocation.Target, out var handlers))
{
@ -678,29 +682,30 @@ namespace Microsoft.AspNetCore.SignalR.Client
Log.ShutdownConnection(_logger);
}
// Fire-and-forget the closed event
RunClosedEvent(connectionState.CloseException);
}
private void RunClosedEvent(Exception closeException)
{
var closed = Closed;
// There is no need to start a new task if there is no Closed event registered
if (closed != null)
{
_ = Task.Run(() =>
{
try
{
Log.InvokingClosedEventHandler(_logger);
closed.Invoke(closeException);
}
catch (Exception ex)
{
Log.ErrorDuringClosedEvent(_logger, ex);
}
});
// Fire-and-forget the closed event
_ = RunClosedEvent(closed, connectionState.CloseException);
}
}
private async Task RunClosedEvent(Action<Exception> closed, Exception closeException)
{
// Dispatch to the thread pool before we invoke the user callback
await AwaitableThreadPool.Yield();
try
{
Log.InvokingClosedEventHandler(_logger);
closed.Invoke(closeException);
}
catch (Exception ex)
{
Log.ErrorDuringClosedEvent(_logger, ex);
}
}
@ -925,7 +930,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
}
else
{
_stopTcs = new TaskCompletionSource<object>();
_stopTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
return StopAsyncCore(timeout);
}
}

View File

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Common\AwaitableThreadPool.cs" Link="AwaitableThreadPool.cs" />
<Compile Include="..\Common\ForceAsyncAwaiter.cs" Link="ForceAsyncAwaiter.cs" />
<Compile Include="..\Common\PipeWriterStream.cs" Link="PipeWriterStream.cs" />
</ItemGroup>

View File

@ -485,9 +485,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
await connection.ReceiveTextAsync(":[\"hello\"]}\u001e");
Assert.True(tcs.Task.IsCompleted);
var response = await tcs.Task;
var response = await tcs.Task.OrTimeout();
Assert.Equal("hello", response);
}