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.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Http.Connections.Internal namespace Microsoft.AspNetCore.Internal
{ {
public static class AwaitableThreadPool public static class AwaitableThreadPool
{ {

View File

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

View File

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

View File

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

View File

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