From 0edf36bd2159a69bbdcab5a806be904b4649d2fd Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 9 Aug 2016 14:49:03 -0700 Subject: [PATCH] Return last block from SocketInput when data is fully consumed - This reduces Kestrel's memory usage for idle connections. --- .../Internal/Http/SocketInput.cs | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketInput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketInput.cs index 61f477ea4a..e73b72b77e 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketInput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketInput.cs @@ -53,18 +53,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http public MemoryPoolBlock IncomingStart() { - const int minimumSize = 2048; - - if (_tail != null && minimumSize <= _tail.Data.Offset + _tail.Data.Count - _tail.End) + lock (_sync) { - _pinned = _tail; - } - else - { - _pinned = _memory.Lease(); - } + const int minimumSize = 2048; - return _pinned; + if (_tail != null && minimumSize <= _tail.Data.Offset + _tail.Data.Count - _tail.End) + { + _pinned = _tail; + } + else + { + _pinned = _memory.Lease(); + } + + return _pinned; + } } public void IncomingComplete(int count, Exception error) @@ -112,14 +115,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http { Debug.Assert(_pinned != null); - if (_pinned != null) + lock (_sync) { - if (_pinned != _tail) + if (_pinned != null) { - _memory.Return(_pinned); - } + if (_pinned != _tail) + { + _memory.Return(_pinned); + } - _pinned = null; + _pinned = null; + } } } @@ -172,18 +178,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http } returnStart = _head; - returnEnd = consumed.Block; - _head = consumed.Block; - _head.Start = consumed.Index; + + var consumedAll = !consumed.IsDefault && consumed.IsEnd; + if (consumedAll && _pinned != _tail) + { + // Everything has been consumed and no data is being written to the + // _tail block, so return all blocks between _head and _tail inclusive. + returnEnd = null; + _head = null; + _tail = null; + } + else + { + returnEnd = consumed.Block; + _head = consumed.Block; + _head.Start = consumed.Index; + } // Must call Subtract() after _head has been advanced, to avoid producer starting too early and growing // buffer beyond max length. _bufferSizeControl?.Subtract(lengthConsumed); } - if (!examined.IsDefault && - examined.IsEnd && - ReadingInput) + // If _head is null, everything has been consumed and examined. + var examinedAll = (!examined.IsDefault && examined.IsEnd) || _head == null; + if (examinedAll && ReadingInput) { _manualResetEvent.Reset();