From 63a945827a2ecb204436670d42609e5bd5542c9a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 11 Jun 2023 11:10:18 -0400 Subject: [PATCH] Optimize zero-byte receives on ManagedWebSocket (#87329) Today you can perform a zero-byte receive on ManagedWebSocket, but doing so still issues a request to the underlying stream with the receive header buffer. That in turn can cause the underlying stream to rent and/or pin a buffer. By special-casing zero-byte reads, we can take advantage of any special-casing in the base stream, and hopefully make it so that when the actual read is performed, the data necessary to satisfy it synchronously is already available. --- .../src/System/Net/WebSockets/ManagedWebSocket.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs index 3b24288..5311fc7 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -697,6 +697,21 @@ namespace System.Net.WebSockets // Make sure we have the first two bytes, which includes the start of the payload length. if (_receiveBufferCount < 2) { + if (payloadBuffer.IsEmpty) + { + // The caller has issued a zero-byte read. The only meaningful reason to do that is to + // wait for data to be available without actually consuming any of it. If we just pass down + // our internal buffer, the underlying stream might end up renting and/or pinning a buffer + // for the duration of the operation, which isn't necessary when we don't actually want to + // consume anything. Instead, we issue a zero-byte read against the underlying stream; + // given that the receive buffer currently stores fewer than the minimum number of bytes + // necessary for a header, it's safe to issue a read (if there were at least the minimum + // number of bytes available, we could end up issuing a read that would erroneously wait + // for data that would never arrive). Once that read completes, we can proceed with any + // other reads necessary, and they'll have a reduced chance of pinning the receive buffer. + await _stream.ReadAsync(Memory.Empty, cancellationToken).ConfigureAwait(false); + } + await EnsureBufferContainsAsync(2, cancellationToken).ConfigureAwait(false); } -- 2.7.4