From 8eefda11b659cc81b18985dff8292be32f95036a Mon Sep 17 00:00:00 2001 From: BrennanConroy Date: Mon, 11 Feb 2019 16:47:26 -0800 Subject: [PATCH 1/3] Copy Kestrel perf changes --- ...soft.AspNetCore.SignalR.Client.Core.csproj | 1 + .../src/Internal/SendUtils.cs | 3 +- ....AspNetCore.Http.Connections.Client.csproj | 1 + .../src/NegotiateProtocol.cs | 27 ++++++------ ...crosoft.AspNetCore.Http.Connections.csproj | 1 + src/SignalR/common/Shared/PipeWriterStream.cs | 3 +- .../common/Shared/SystemTextJsonExtensions.cs | 9 ++-- .../common/Shared/ValueTaskExtensions.cs | 42 +++++++++++++++++++ .../src/Protocol/HandshakeProtocol.cs | 19 +++++---- .../Core/src/DefaultHubLifetimeManager.cs | 7 ++-- .../Core/src/Internal/DefaultHubDispatcher.cs | 3 +- .../Microsoft.AspNetCore.SignalR.Core.csproj | 1 + src/SignalR/server/Core/src/StreamTracker.cs | 3 +- ...pNetCore.SignalR.StackExchangeRedis.csproj | 1 + .../src/RedisHubLifetimeManager.cs | 11 ++--- 15 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 src/SignalR/common/Shared/ValueTaskExtensions.cs diff --git a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj index 333ff3f1017d..ca04b50a3098 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj +++ b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj @@ -13,6 +13,7 @@ + diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs index 2af7ab5ee036..f3f2f2e3febf 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs @@ -9,6 +9,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Http.Connections.Client.Internal @@ -99,7 +100,7 @@ public ReadOnlySequenceContent(in ReadOnlySequence buffer) protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { - return stream.WriteAsync(_buffer).AsTask(); + return stream.WriteAsync(_buffer).GetAsTask(); } protected override bool TryComputeLength(out long length) diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj b/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj index 983ea8d4b2d3..5142c8f2c408 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj @@ -12,6 +12,7 @@ + diff --git a/src/SignalR/common/Http.Connections.Common/src/NegotiateProtocol.cs b/src/SignalR/common/Http.Connections.Common/src/NegotiateProtocol.cs index d473f5c93cc0..b5950ee53603 100644 --- a/src/SignalR/common/Http.Connections.Common/src/NegotiateProtocol.cs +++ b/src/SignalR/common/Http.Connections.Common/src/NegotiateProtocol.cs @@ -13,24 +13,25 @@ namespace Microsoft.AspNetCore.Http.Connections { public static class NegotiateProtocol { + // Use C#7.3's ReadOnlySpan optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/ private const string ConnectionIdPropertyName = "connectionId"; - private static readonly byte[] ConnectionIdPropertyNameBytes = Encoding.UTF8.GetBytes(ConnectionIdPropertyName); + private static ReadOnlySpan ConnectionIdPropertyNameBytes => new byte[] { (byte)'c', (byte)'o', (byte)'n', (byte)'n', (byte)'e', (byte)'c', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'I', (byte)'d' }; private const string UrlPropertyName = "url"; - private static readonly byte[] UrlPropertyNameBytes = Encoding.UTF8.GetBytes(UrlPropertyName); + private static ReadOnlySpan UrlPropertyNameBytes => new byte[] { (byte)'u', (byte)'r', (byte)'l' }; private const string AccessTokenPropertyName = "accessToken"; - private static readonly byte[] AccessTokenPropertyNameBytes = Encoding.UTF8.GetBytes(AccessTokenPropertyName); + private static ReadOnlySpan AccessTokenPropertyNameBytes => new byte[] { (byte)'a', (byte)'c', (byte)'c', (byte)'e', (byte)'s', (byte)'s', (byte)'T', (byte)'o', (byte)'k', (byte)'e', (byte)'n' }; private const string AvailableTransportsPropertyName = "availableTransports"; - private static readonly byte[] AvailableTransportsPropertyNameBytes = Encoding.UTF8.GetBytes(AvailableTransportsPropertyName); + private static ReadOnlySpan AvailableTransportsPropertyNameBytes => new byte[] { (byte)'a', (byte)'v', (byte)'a', (byte)'i', (byte)'l', (byte)'a', (byte)'b', (byte)'l', (byte)'e', (byte)'T', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'p', (byte)'o', (byte)'r', (byte)'t', (byte)'s' }; private const string TransportPropertyName = "transport"; - private static readonly byte[] TransportPropertyNameBytes = Encoding.UTF8.GetBytes(TransportPropertyName); + private static ReadOnlySpan TransportPropertyNameBytes => new byte[] { (byte)'t', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'p', (byte)'o', (byte)'r', (byte)'t' }; private const string TransferFormatsPropertyName = "transferFormats"; - private static readonly byte[] TransferFormatsPropertyNameBytes = Encoding.UTF8.GetBytes(TransferFormatsPropertyName); + private static ReadOnlySpan TransferFormatsPropertyNameBytes => new byte[] { (byte)'t', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'f', (byte)'e', (byte)'r', (byte)'F', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'t', (byte)'s' }; private const string ErrorPropertyName = "error"; - private static readonly byte[] ErrorPropertyNameBytes = Encoding.UTF8.GetBytes(ErrorPropertyName); + private static ReadOnlySpan ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' }; // Used to detect ASP.NET SignalR Server connection attempt private const string ProtocolVersionPropertyName = "ProtocolVersion"; - private static readonly byte[] ProtocolVersionPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolVersionPropertyName); + private static ReadOnlySpan ProtocolVersionPropertyNameBytes => new byte[] { (byte)'P', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l', (byte)'V', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' }; public static void WriteResponse(NegotiationResponse response, IBufferWriter output) { @@ -114,15 +115,15 @@ public static NegotiationResponse ParseResponse(ReadOnlySpan content) if (memberName.SequenceEqual(UrlPropertyNameBytes)) { - url = reader.ReadAsString(UrlPropertyNameBytes); + url = reader.ReadAsString(UrlPropertyName); } else if (memberName.SequenceEqual(AccessTokenPropertyNameBytes)) { - accessToken = reader.ReadAsString(AccessTokenPropertyNameBytes); + accessToken = reader.ReadAsString(AccessTokenPropertyName); } else if (memberName.SequenceEqual(ConnectionIdPropertyNameBytes)) { - connectionId = reader.ReadAsString(ConnectionIdPropertyNameBytes); + connectionId = reader.ReadAsString(ConnectionIdPropertyName); } else if (memberName.SequenceEqual(AvailableTransportsPropertyNameBytes)) { @@ -144,7 +145,7 @@ public static NegotiationResponse ParseResponse(ReadOnlySpan content) } else if (memberName.SequenceEqual(ErrorPropertyNameBytes)) { - error = reader.ReadAsString(ErrorPropertyNameBytes); + error = reader.ReadAsString(ErrorPropertyName); } else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes)) { @@ -215,7 +216,7 @@ private static AvailableTransport ParseAvailableTransport(ref Utf8JsonReader rea if (memberName.SequenceEqual(TransportPropertyNameBytes)) { - availableTransport.Transport = reader.ReadAsString(TransportPropertyNameBytes); + availableTransport.Transport = reader.ReadAsString(TransportPropertyName); } else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes)) { diff --git a/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj b/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj index 42845dcee4d1..c8cbe9569d4c 100644 --- a/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj +++ b/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj @@ -13,6 +13,7 @@ + diff --git a/src/SignalR/common/Shared/PipeWriterStream.cs b/src/SignalR/common/Shared/PipeWriterStream.cs index 10bfa18b9609..a85ff0b3c87e 100644 --- a/src/SignalR/common/Shared/PipeWriterStream.cs +++ b/src/SignalR/common/Shared/PipeWriterStream.cs @@ -6,6 +6,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Internal; namespace System.IO.Pipelines { @@ -57,7 +58,7 @@ public override void Write(byte[] buffer, int offset, int count) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - return WriteCoreAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask(); + return WriteCoreAsync(buffer.AsMemory(offset, count), cancellationToken).GetAsTask(); } #if NETCOREAPP3_0 diff --git a/src/SignalR/common/Shared/SystemTextJsonExtensions.cs b/src/SignalR/common/Shared/SystemTextJsonExtensions.cs index 68ff75b50eea..5b67925e0859 100644 --- a/src/SignalR/common/Shared/SystemTextJsonExtensions.cs +++ b/src/SignalR/common/Shared/SystemTextJsonExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.IO; using System.Text; using System.Text.Json; @@ -70,19 +71,19 @@ public static void Skip(this ref Utf8JsonReader reader) } } - public static string ReadAsString(this ref Utf8JsonReader reader, byte[] propertyName) + public static string ReadAsString(this ref Utf8JsonReader reader, string propertyName) { reader.Read(); if (reader.TokenType != JsonTokenType.String) { - throw new InvalidDataException($"Expected '{Encoding.UTF8.GetString(propertyName)}' to be of type {JsonTokenType.String}."); + throw new InvalidDataException($"Expected '{propertyName}' to be of type {JsonTokenType.String}."); } return reader.GetString(); } - public static int? ReadAsInt32(this ref Utf8JsonReader reader, byte[] propertyName) + public static int? ReadAsInt32(this ref Utf8JsonReader reader, string propertyName) { reader.Read(); @@ -93,7 +94,7 @@ public static string ReadAsString(this ref Utf8JsonReader reader, byte[] propert if (reader.TokenType != JsonTokenType.Number) { - throw new InvalidDataException($"Expected '{Encoding.UTF8.GetString(propertyName)}' to be of type {JsonTokenType.Number}."); + throw new InvalidDataException($"Expected '{propertyName}' to be of type {JsonTokenType.Number}."); } return reader.GetInt32(); diff --git a/src/SignalR/common/Shared/ValueTaskExtensions.cs b/src/SignalR/common/Shared/ValueTaskExtensions.cs new file mode 100644 index 000000000000..7982f5b96361 --- /dev/null +++ b/src/SignalR/common/Shared/ValueTaskExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO.Pipelines; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Internal +{ + internal static class ValueTaskExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Task GetAsTask(this in ValueTask valueTask) + { + if (valueTask.IsCompletedSuccessfully) + { + // Signal consumption to the IValueTaskSource + valueTask.GetAwaiter().GetResult(); + return Task.CompletedTask; + } + else + { + return valueTask.AsTask(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Task GetAsTask(this in ValueTask valueTask) + { + if (valueTask.IsCompletedSuccessfully) + { + // Signal consumption to the IValueTaskSource + valueTask.GetAwaiter().GetResult(); + return Task.CompletedTask; + } + else + { + return valueTask.AsTask(); + } + } + } +} \ No newline at end of file diff --git a/src/SignalR/common/SignalR.Common/src/Protocol/HandshakeProtocol.cs b/src/SignalR/common/SignalR.Common/src/Protocol/HandshakeProtocol.cs index 0b4cd7cefb55..c591757a9a46 100644 --- a/src/SignalR/common/SignalR.Common/src/Protocol/HandshakeProtocol.cs +++ b/src/SignalR/common/SignalR.Common/src/Protocol/HandshakeProtocol.cs @@ -17,16 +17,17 @@ namespace Microsoft.AspNetCore.SignalR.Protocol /// public static class HandshakeProtocol { + // Use C#7.3's ReadOnlySpan optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/ private const string ProtocolPropertyName = "protocol"; - private static readonly byte[] ProtocolPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolPropertyName); + private static ReadOnlySpan ProtocolPropertyNameBytes => new byte[] { (byte)'p', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l' }; private const string ProtocolVersionPropertyName = "version"; - private static readonly byte[] ProtocolVersionPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolVersionPropertyName); + private static ReadOnlySpan ProtocolVersionPropertyNameBytes => new byte[] { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' }; private const string MinorVersionPropertyName = "minorVersion"; - private static readonly byte[] MinorVersionPropertyNameBytes = Encoding.UTF8.GetBytes(MinorVersionPropertyName); + private static ReadOnlySpan MinorVersionPropertyNameBytes => new byte[] { (byte)'m', (byte)'i', (byte)'n', (byte)'o', (byte)'r', (byte)'V', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' }; private const string ErrorPropertyName = "error"; - private static readonly byte[] ErrorPropertyNameBytes = Encoding.UTF8.GetBytes(ErrorPropertyName); + private static ReadOnlySpan ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' }; private const string TypePropertyName = "type"; - private static readonly byte[] TypePropertyNameBytes = Encoding.UTF8.GetBytes(TypePropertyName); + private static ReadOnlySpan TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' }; private static ConcurrentDictionary> _messageCache = new ConcurrentDictionary>(); @@ -128,11 +129,11 @@ public static bool TryParseResponseMessage(ref ReadOnlySequence buffer, ou } else if (memberName.SequenceEqual(ErrorPropertyNameBytes)) { - error = reader.ReadAsString(ErrorPropertyNameBytes); + error = reader.ReadAsString(ErrorPropertyName); } else if (memberName.SequenceEqual(MinorVersionPropertyNameBytes)) { - minorVersion = reader.ReadAsInt32(MinorVersionPropertyNameBytes); + minorVersion = reader.ReadAsInt32(MinorVersionPropertyName); } else { @@ -183,11 +184,11 @@ public static bool TryParseRequestMessage(ref ReadOnlySequence buffer, out if (memberName.SequenceEqual(ProtocolPropertyNameBytes)) { - protocol = reader.ReadAsString(ProtocolPropertyNameBytes); + protocol = reader.ReadAsString(ProtocolPropertyName); } else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes)) { - protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyNameBytes); + protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyName); } else { diff --git a/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs b/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs index 3c835ab933e2..a4c5367b3b6c 100644 --- a/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs +++ b/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.Logging; @@ -112,7 +113,7 @@ private Task SendToAllConnections(string methodName, object[] args, Func(); } - tasks.Add(task.AsTask()); + tasks.Add(task.GetAsTask()); } } @@ -151,7 +152,7 @@ private void SendToGroupConnections(string methodName, object[] args, Concurrent tasks = new List(); } - tasks.Add(task.AsTask()); + tasks.Add(task.GetAsTask()); } } } @@ -175,7 +176,7 @@ public override Task SendConnectionAsync(string connectionId, string methodName, // Write message directly to connection without caching it in memory var message = CreateInvocationMessage(methodName, args); - return connection.WriteAsync(message).AsTask(); + return connection.WriteAsync(message).GetAsTask(); } /// diff --git a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs index e137cc59986d..5ca3a321dc0c 100644 --- a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs +++ b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs @@ -11,6 +11,7 @@ using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; @@ -186,7 +187,7 @@ private Task ProcessInvocation(HubConnectionContext connection, // Send an error to the client. Then let the normal completion process occur Log.UnknownHubMethod(_logger, hubMethodInvocationMessage.Target); return connection.WriteAsync(CompletionMessage.WithError( - hubMethodInvocationMessage.InvocationId, $"Unknown hub method '{hubMethodInvocationMessage.Target}'")).AsTask(); + hubMethodInvocationMessage.InvocationId, $"Unknown hub method '{hubMethodInvocationMessage.Target}'")).GetAsTask(); } else { diff --git a/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj b/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj index 3bfcfd26e7d5..5a70d41bc19f 100644 --- a/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj +++ b/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj @@ -8,6 +8,7 @@ + diff --git a/src/SignalR/server/Core/src/StreamTracker.cs b/src/SignalR/server/Core/src/StreamTracker.cs index a507fde54be1..4735b6bc3570 100644 --- a/src/SignalR/server/Core/src/StreamTracker.cs +++ b/src/SignalR/server/Core/src/StreamTracker.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Threading.Channels; using System.Threading.Tasks; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; namespace Microsoft.AspNetCore.SignalR @@ -95,7 +96,7 @@ public object GetReaderAsObject() public Task WriteToStream(object o) { - return _channel.Writer.WriteAsync((T)o).AsTask(); + return _channel.Writer.WriteAsync((T)o).GetAsTask(); } public void TryComplete(Exception ex) diff --git a/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj b/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj index 526238dfb55d..4fd4fc3bb13f 100644 --- a/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj +++ b/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj @@ -9,6 +9,7 @@ + diff --git a/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs b/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs index 17b462bfd0d9..0f6b0f3f56a5 100644 --- a/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs +++ b/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.StackExchangeRedis.Internal; using Microsoft.Extensions.Logging; @@ -125,7 +126,7 @@ public override Task SendConnectionAsync(string connectionId, string methodName, var connection = _connections[connectionId]; if (connection != null) { - return connection.WriteAsync(new InvocationMessage(methodName, args)).AsTask(); + return connection.WriteAsync(new InvocationMessage(methodName, args)).GetAsTask(); } var message = _protocol.WriteInvocation(methodName, args); @@ -359,7 +360,7 @@ private async Task SubscribeToAll() { if (invocation.ExcludedConnectionIds == null || !invocation.ExcludedConnectionIds.Contains(connection.ConnectionId)) { - tasks.Add(connection.WriteAsync(invocation.Message).AsTask()); + tasks.Add(connection.WriteAsync(invocation.Message).GetAsTask()); } } @@ -429,7 +430,7 @@ private async Task SubscribeToConnection(HubConnectionContext connection) channel.OnMessage(channelMessage => { var invocation = _protocol.ReadInvocation((byte[])channelMessage.Message); - return connection.WriteAsync(invocation.Message).AsTask(); + return connection.WriteAsync(invocation.Message).GetAsTask(); }); } @@ -450,7 +451,7 @@ private Task SubscribeToUser(HubConnectionContext connection) var tasks = new List(); foreach (var userConnection in subscriptions) { - tasks.Add(userConnection.WriteAsync(invocation.Message).AsTask()); + tasks.Add(userConnection.WriteAsync(invocation.Message).GetAsTask()); } await Task.WhenAll(tasks); @@ -481,7 +482,7 @@ private async Task SubscribeToGroupAsync(string groupChannel, HubConnectionStore continue; } - tasks.Add(groupConnection.WriteAsync(invocation.Message).AsTask()); + tasks.Add(groupConnection.WriteAsync(invocation.Message).GetAsTask()); } await Task.WhenAll(tasks); From 87a89753732e4d00375abff4e423f1e302db365c Mon Sep 17 00:00:00 2001 From: BrennanConroy Date: Mon, 11 Feb 2019 17:22:55 -0800 Subject: [PATCH 2/3] fb --- .../src/Internal/SendUtils.cs | 3 +-- ...soft.AspNetCore.Http.Connections.Client.csproj | 1 - src/SignalR/common/Shared/PipeWriterStream.cs | 2 +- src/SignalR/common/Shared/ValueTaskExtensions.cs | 15 --------------- .../server/Core/src/DefaultHubLifetimeManager.cs | 7 +++---- .../Core/src/Internal/DefaultHubDispatcher.cs | 3 +-- .../src/Microsoft.AspNetCore.SignalR.Core.csproj | 1 - src/SignalR/server/Core/src/StreamTracker.cs | 2 +- ...t.AspNetCore.SignalR.StackExchangeRedis.csproj | 1 - .../src/RedisHubLifetimeManager.cs | 11 +++++------ 10 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs index f3f2f2e3febf..2af7ab5ee036 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs @@ -9,7 +9,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Http.Connections.Client.Internal @@ -100,7 +99,7 @@ public ReadOnlySequenceContent(in ReadOnlySequence buffer) protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { - return stream.WriteAsync(_buffer).GetAsTask(); + return stream.WriteAsync(_buffer).AsTask(); } protected override bool TryComputeLength(out long length) diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj b/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj index 5142c8f2c408..983ea8d4b2d3 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj @@ -12,7 +12,6 @@ - diff --git a/src/SignalR/common/Shared/PipeWriterStream.cs b/src/SignalR/common/Shared/PipeWriterStream.cs index a85ff0b3c87e..c569d09ed2d1 100644 --- a/src/SignalR/common/Shared/PipeWriterStream.cs +++ b/src/SignalR/common/Shared/PipeWriterStream.cs @@ -58,7 +58,7 @@ public override void Write(byte[] buffer, int offset, int count) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - return WriteCoreAsync(buffer.AsMemory(offset, count), cancellationToken).GetAsTask(); + return WriteCoreAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask(); } #if NETCOREAPP3_0 diff --git a/src/SignalR/common/Shared/ValueTaskExtensions.cs b/src/SignalR/common/Shared/ValueTaskExtensions.cs index 7982f5b96361..114be73f72f6 100644 --- a/src/SignalR/common/Shared/ValueTaskExtensions.cs +++ b/src/SignalR/common/Shared/ValueTaskExtensions.cs @@ -23,20 +23,5 @@ public static Task GetAsTask(this in ValueTask valueTask) return valueTask.AsTask(); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task GetAsTask(this in ValueTask valueTask) - { - if (valueTask.IsCompletedSuccessfully) - { - // Signal consumption to the IValueTaskSource - valueTask.GetAwaiter().GetResult(); - return Task.CompletedTask; - } - else - { - return valueTask.AsTask(); - } - } } } \ No newline at end of file diff --git a/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs b/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs index a4c5367b3b6c..3c835ab933e2 100644 --- a/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs +++ b/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.Logging; @@ -113,7 +112,7 @@ private Task SendToAllConnections(string methodName, object[] args, Func(); } - tasks.Add(task.GetAsTask()); + tasks.Add(task.AsTask()); } } @@ -152,7 +151,7 @@ private void SendToGroupConnections(string methodName, object[] args, Concurrent tasks = new List(); } - tasks.Add(task.GetAsTask()); + tasks.Add(task.AsTask()); } } } @@ -176,7 +175,7 @@ public override Task SendConnectionAsync(string connectionId, string methodName, // Write message directly to connection without caching it in memory var message = CreateInvocationMessage(methodName, args); - return connection.WriteAsync(message).GetAsTask(); + return connection.WriteAsync(message).AsTask(); } /// diff --git a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs index 5ca3a321dc0c..e137cc59986d 100644 --- a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs +++ b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs @@ -11,7 +11,6 @@ using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; @@ -187,7 +186,7 @@ private Task ProcessInvocation(HubConnectionContext connection, // Send an error to the client. Then let the normal completion process occur Log.UnknownHubMethod(_logger, hubMethodInvocationMessage.Target); return connection.WriteAsync(CompletionMessage.WithError( - hubMethodInvocationMessage.InvocationId, $"Unknown hub method '{hubMethodInvocationMessage.Target}'")).GetAsTask(); + hubMethodInvocationMessage.InvocationId, $"Unknown hub method '{hubMethodInvocationMessage.Target}'")).AsTask(); } else { diff --git a/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj b/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj index 5a70d41bc19f..3bfcfd26e7d5 100644 --- a/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj +++ b/src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj @@ -8,7 +8,6 @@ - diff --git a/src/SignalR/server/Core/src/StreamTracker.cs b/src/SignalR/server/Core/src/StreamTracker.cs index 4735b6bc3570..b9b7b1dd248e 100644 --- a/src/SignalR/server/Core/src/StreamTracker.cs +++ b/src/SignalR/server/Core/src/StreamTracker.cs @@ -96,7 +96,7 @@ public object GetReaderAsObject() public Task WriteToStream(object o) { - return _channel.Writer.WriteAsync((T)o).GetAsTask(); + return _channel.Writer.WriteAsync((T)o).AsTask(); } public void TryComplete(Exception ex) diff --git a/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj b/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj index 4fd4fc3bb13f..526238dfb55d 100644 --- a/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj +++ b/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj @@ -9,7 +9,6 @@ - diff --git a/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs b/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs index 0f6b0f3f56a5..17b462bfd0d9 100644 --- a/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs +++ b/src/SignalR/server/StackExchangeRedis/src/RedisHubLifetimeManager.cs @@ -8,7 +8,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.StackExchangeRedis.Internal; using Microsoft.Extensions.Logging; @@ -126,7 +125,7 @@ public override Task SendConnectionAsync(string connectionId, string methodName, var connection = _connections[connectionId]; if (connection != null) { - return connection.WriteAsync(new InvocationMessage(methodName, args)).GetAsTask(); + return connection.WriteAsync(new InvocationMessage(methodName, args)).AsTask(); } var message = _protocol.WriteInvocation(methodName, args); @@ -360,7 +359,7 @@ private async Task SubscribeToAll() { if (invocation.ExcludedConnectionIds == null || !invocation.ExcludedConnectionIds.Contains(connection.ConnectionId)) { - tasks.Add(connection.WriteAsync(invocation.Message).GetAsTask()); + tasks.Add(connection.WriteAsync(invocation.Message).AsTask()); } } @@ -430,7 +429,7 @@ private async Task SubscribeToConnection(HubConnectionContext connection) channel.OnMessage(channelMessage => { var invocation = _protocol.ReadInvocation((byte[])channelMessage.Message); - return connection.WriteAsync(invocation.Message).GetAsTask(); + return connection.WriteAsync(invocation.Message).AsTask(); }); } @@ -451,7 +450,7 @@ private Task SubscribeToUser(HubConnectionContext connection) var tasks = new List(); foreach (var userConnection in subscriptions) { - tasks.Add(userConnection.WriteAsync(invocation.Message).GetAsTask()); + tasks.Add(userConnection.WriteAsync(invocation.Message).AsTask()); } await Task.WhenAll(tasks); @@ -482,7 +481,7 @@ private async Task SubscribeToGroupAsync(string groupChannel, HubConnectionStore continue; } - tasks.Add(groupConnection.WriteAsync(invocation.Message).GetAsTask()); + tasks.Add(groupConnection.WriteAsync(invocation.Message).AsTask()); } await Task.WhenAll(tasks); From dc51132bbf9a62cf9b4de3177731b4e4f4a17db9 Mon Sep 17 00:00:00 2001 From: Brennan Conroy Date: Wed, 13 Feb 2019 10:52:58 -0800 Subject: [PATCH 3/3] fb --- .../src/Internal/ServerSentEventsMessageParser.cs | 4 ++-- src/SignalR/common/Shared/ValueTaskExtensions.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsMessageParser.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsMessageParser.cs index 3038059d4a17..756f69d7482d 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsMessageParser.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsMessageParser.cs @@ -15,8 +15,8 @@ internal class ServerSentEventsMessageParser private const byte ByteLF = (byte)'\n'; private const byte ByteColon = (byte)':'; - private static readonly byte[] _dataPrefix = Encoding.UTF8.GetBytes("data: "); - private static readonly byte[] _sseLineEnding = Encoding.UTF8.GetBytes("\r\n"); + private static ReadOnlySpan _dataPrefix => new byte[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', (byte)' ' }; + private static ReadOnlySpan _sseLineEnding => new byte[] { (byte)'\r', (byte)'\n' }; private static readonly byte[] _newLine = Encoding.UTF8.GetBytes(Environment.NewLine); private InternalParseState _internalParserState = InternalParseState.ReadMessagePayload; diff --git a/src/SignalR/common/Shared/ValueTaskExtensions.cs b/src/SignalR/common/Shared/ValueTaskExtensions.cs index 114be73f72f6..0c8c8e4669af 100644 --- a/src/SignalR/common/Shared/ValueTaskExtensions.cs +++ b/src/SignalR/common/Shared/ValueTaskExtensions.cs @@ -12,6 +12,7 @@ internal static class ValueTaskExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Task GetAsTask(this in ValueTask valueTask) { + // Try to avoid the allocation from AsTask if (valueTask.IsCompletedSuccessfully) { // Signal consumption to the IValueTaskSource @@ -24,4 +25,4 @@ public static Task GetAsTask(this in ValueTask valueTask) } } } -} \ No newline at end of file +}