diff --git a/Directory.Build.props b/Directory.Build.props
index 170752b08d..7aaac5fb80 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,6 +7,10 @@
+
+ $(DefineConstants);INNER_LOOP
+
+
Microsoft ASP.NET Core
https://github.com/aspnet/KestrelHttpServer
diff --git a/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs b/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs
index 69df7fdb88..2134a7d5b4 100644
--- a/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs
+++ b/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs
@@ -26,6 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private MemoryPool _memoryPool;
+ private DuplexPipe.DuplexPipePair _pair;
+
[Params(
BenchmarkTypes.TechEmpowerPlaintext,
BenchmarkTypes.PlaintextChunked,
@@ -115,7 +117,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
_memoryPool = KestrelMemoryPool.Create();
var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
- var pair = DuplexPipe.CreateConnectionPair(options, options);
+ _pair = DuplexPipe.CreateConnectionPair(options, options);
var serviceContext = new ServiceContext
{
@@ -131,8 +133,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
ConnectionFeatures = new FeatureCollection(),
MemoryPool = _memoryPool,
TimeoutControl = new MockTimeoutControl(),
- Application = pair.Application,
- Transport = pair.Transport
+ Application = _pair.Application,
+ Transport = _pair.Transport
});
http1Connection.Reset();
@@ -143,6 +145,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[IterationCleanup]
public void Cleanup()
{
+ _pair.Application.Input.Complete();
+ _pair.Application.Output.Complete();
+ _pair.Transport.Input.Complete();
+ _pair.Transport.Output.Complete();
_memoryPool.Dispose();
}
diff --git a/build/dependencies.props b/build/dependencies.props
index 5f3f478827..014c60bb3e 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -18,7 +18,7 @@
2.2.0-preview1-34066
2.2.0-preview1-34066
2.2.0-preview1-34066
- 2.2.0-preview1-34066
+ 2.2.0-a-preview1-pool-cleanup-16484
2.2.0-preview1-34066
2.2.0-preview1-34066
2.2.0-preview1-34066
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 6b85b2cf04..cf4c0f9e56 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,8 +1,5 @@
-
- $(DefineConstants);INNER_LOOP
-
diff --git a/src/Kestrel.Core/Adapter/Internal/LoggingStream.cs b/src/Kestrel.Core/Adapter/Internal/LoggingStream.cs
index 0b5fdf3975..f9225b6f48 100644
--- a/src/Kestrel.Core/Adapter/Internal/LoggingStream.cs
+++ b/src/Kestrel.Core/Adapter/Internal/LoggingStream.cs
@@ -148,6 +148,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
private void Log(string method, ReadOnlySpan buffer)
{
+ if (!_logger.IsEnabled(LogLevel.Debug))
+ {
+ return;
+ }
+
var builder = new StringBuilder($"{method}[{buffer.Length}] ");
// Write the hex
diff --git a/src/Kestrel.Transport.Abstractions/Internal/KestrelMemoryPool.cs b/src/Kestrel.Transport.Abstractions/Internal/KestrelMemoryPool.cs
index f53c15d543..b3577f2c0a 100644
--- a/src/Kestrel.Transport.Abstractions/Internal/KestrelMemoryPool.cs
+++ b/src/Kestrel.Transport.Abstractions/Internal/KestrelMemoryPool.cs
@@ -1,13 +1,26 @@
// 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.Buffers;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
{
public static class KestrelMemoryPool
{
- public static MemoryPool Create() => new SlabMemoryPool();
+ public static MemoryPool Create()
+ {
+#if DEBUG
+ return new DiagnosticMemoryPool(CreateSlabMemoryPool());
+#else
+ return CreateSlabMemoryPool();
+#endif
+ }
+
+ public static MemoryPool CreateSlabMemoryPool()
+ {
+ return new SlabMemoryPool();
+ }
public static readonly int MinimumSegmentSize = 4096;
}
diff --git a/src/Kestrel.Transport.Libuv/AssemblyInfo.cs b/src/Kestrel.Transport.Libuv/AssemblyInfo.cs
new file mode 100644
index 0000000000..29df5a481f
--- /dev/null
+++ b/src/Kestrel.Transport.Libuv/AssemblyInfo.cs
@@ -0,0 +1,6 @@
+// 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.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Kestrel.Transport.Libuv/Internal/LibuvThread.cs b/src/Kestrel.Transport.Libuv/Internal/LibuvThread.cs
index f8145ae22d..43b8f8912e 100644
--- a/src/Kestrel.Transport.Libuv/Internal/LibuvThread.cs
+++ b/src/Kestrel.Transport.Libuv/Internal/LibuvThread.cs
@@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
private readonly object _startSync = new object();
private bool _stopImmediate = false;
private bool _initCompleted = false;
- private ExceptionDispatchInfo _closeError;
+ private Exception _closeError;
private readonly ILibuvTrace _log;
public LibuvThread(LibuvTransport transport)
@@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
#endif
QueueCloseHandle = PostCloseHandle;
QueueCloseAsyncHandle = EnqueueCloseHandle;
- MemoryPool = KestrelMemoryPool.Create();
+ MemoryPool = transport.TransportOptions.MemoryPoolFactory();
WriteReqPool = new WriteReqPool(this, _log);
}
@@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
public List Requests { get; } = new List();
#endif
- public ExceptionDispatchInfo FatalError { get { return _closeError; } }
+ public Exception FatalError => _closeError;
public Action, IntPtr> QueueCloseHandle { get; }
@@ -133,7 +133,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
}
}
- _closeError?.Throw();
+ if (_closeError != null)
+ {
+ ExceptionDispatchInfo.Capture(_closeError).Throw();
+ }
}
#if DEBUG && !INNER_LOOP
@@ -323,14 +326,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
}
catch (Exception ex)
{
- _closeError = ExceptionDispatchInfo.Capture(ex);
+ _closeError = ex;
// Request shutdown so we can rethrow this exception
// in Stop which should be observable.
_appLifetime.StopApplication();
}
finally
{
- MemoryPool.Dispose();
+ try
+ {
+ MemoryPool.Dispose();
+ }
+ catch (Exception ex)
+ {
+ _closeError = _closeError == null ? ex : new AggregateException(_closeError, ex);
+ }
WriteReqPool.Dispose();
_threadTcs.SetResult(null);
diff --git a/src/Kestrel.Transport.Libuv/LibuvTransportOptions.cs b/src/Kestrel.Transport.Libuv/LibuvTransportOptions.cs
index db157d39dd..a040dea87b 100644
--- a/src/Kestrel.Transport.Libuv/LibuvTransportOptions.cs
+++ b/src/Kestrel.Transport.Libuv/LibuvTransportOptions.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Buffers;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv
{
@@ -18,6 +20,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv
///
public int ThreadCount { get; set; } = ProcessorThreadCount;
+ internal Func> MemoryPoolFactory { get; set; } = () => KestrelMemoryPool.Create();
+
private static int ProcessorThreadCount
{
get
diff --git a/src/Kestrel.Transport.Sockets/AssemblyInfo.cs b/src/Kestrel.Transport.Sockets/AssemblyInfo.cs
new file mode 100644
index 0000000000..89edc0376a
--- /dev/null
+++ b/src/Kestrel.Transport.Sockets/AssemblyInfo.cs
@@ -0,0 +1,6 @@
+// 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.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Kestrel.Transport.Sockets/SocketTransport.cs b/src/Kestrel.Transport.Sockets/SocketTransport.cs
index 4882b47114..1c17258ca4 100644
--- a/src/Kestrel.Transport.Sockets/SocketTransport.cs
+++ b/src/Kestrel.Transport.Sockets/SocketTransport.cs
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
{
private static readonly PipeScheduler[] ThreadPoolSchedulerArray = new PipeScheduler[] { PipeScheduler.ThreadPool };
- private readonly MemoryPool _memoryPool = KestrelMemoryPool.Create();
+ private readonly MemoryPool _memoryPool;
private readonly IEndPointInformation _endPointInformation;
private readonly IConnectionDispatcher _dispatcher;
private readonly IApplicationLifetime _appLifetime;
@@ -39,7 +39,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
IConnectionDispatcher dispatcher,
IApplicationLifetime applicationLifetime,
int ioQueueCount,
- ISocketsTrace trace)
+ ISocketsTrace trace,
+ MemoryPool memoryPool)
{
Debug.Assert(endPointInformation != null);
Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint);
@@ -51,6 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
_dispatcher = dispatcher;
_appLifetime = applicationLifetime;
_trace = trace;
+ _memoryPool = memoryPool;
if (ioQueueCount > 0)
{
diff --git a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs
index 7ddd1d918d..2e0e8170ef 100644
--- a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs
+++ b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs
@@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
throw new ArgumentNullException(nameof(dispatcher));
}
- return new SocketTransport(endPointInformation, dispatcher, _appLifetime, _options.IOQueueCount, _trace);
+ return new SocketTransport(endPointInformation, dispatcher, _appLifetime, _options.IOQueueCount, _trace, _options.MemoryPoolFactory());
}
}
}
diff --git a/src/Kestrel.Transport.Sockets/SocketTransportOptions.cs b/src/Kestrel.Transport.Sockets/SocketTransportOptions.cs
index b6cec0a6d7..2dad914423 100644
--- a/src/Kestrel.Transport.Sockets/SocketTransportOptions.cs
+++ b/src/Kestrel.Transport.Sockets/SocketTransportOptions.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Buffers;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
{
@@ -14,5 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
/// Defaults to rounded down and clamped between 1 and 16.
///
public int IOQueueCount { get; set; } = Math.Min(Environment.ProcessorCount, 16);
+
+ internal Func> MemoryPoolFactory { get; set; } = () => KestrelMemoryPool.Create();
}
}
diff --git a/test/Kestrel.Core.Tests/Http2ConnectionTests.cs b/test/Kestrel.Core.Tests/Http2ConnectionTests.cs
index 341c9b21e6..43cd9078d2 100644
--- a/test/Kestrel.Core.Tests/Http2ConnectionTests.cs
+++ b/test/Kestrel.Core.Tests/Http2ConnectionTests.cs
@@ -287,6 +287,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void Dispose()
{
+ _pair.Application.Input.Complete();
+ _pair.Application.Output.Complete();
+ _pair.Transport.Input.Complete();
+ _pair.Transport.Output.Complete();
_memoryPool.Dispose();
}
@@ -1213,7 +1217,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
-
+
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
diff --git a/test/Kestrel.Core.Tests/PipelineExtensionTests.cs b/test/Kestrel.Core.Tests/PipelineExtensionTests.cs
index e3a89832da..11f7209ac2 100644
--- a/test/Kestrel.Core.Tests/PipelineExtensionTests.cs
+++ b/test/Kestrel.Core.Tests/PipelineExtensionTests.cs
@@ -26,6 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void Dispose()
{
+ _pipe.Reader.Complete();
+ _pipe.Writer.Complete();
_memoryPool.Dispose();
}
diff --git a/test/Kestrel.Core.Tests/TestInput.cs b/test/Kestrel.Core.Tests/TestInput.cs
index 1295121548..1916b62753 100644
--- a/test/Kestrel.Core.Tests/TestInput.cs
+++ b/test/Kestrel.Core.Tests/TestInput.cs
@@ -68,6 +68,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void Dispose()
{
+ Application.Input.Complete();
+ Application.Output.Complete();
+ Transport.Input.Complete();
+ Transport.Output.Complete();
_memoryPool.Dispose();
}
}
diff --git a/test/Kestrel.FunctionalTests/ChunkedRequestTests.cs b/test/Kestrel.FunctionalTests/ChunkedRequestTests.cs
index 8dba8331e1..3bc0c9d286 100644
--- a/test/Kestrel.FunctionalTests/ChunkedRequestTests.cs
+++ b/test/Kestrel.FunctionalTests/ChunkedRequestTests.cs
@@ -12,11 +12,16 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
+using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
public class ChunkedRequestTests : LoggedTest
{
+ public ChunkedRequestTests(ITestOutputHelper output) : base(output)
+ {
+ }
+
public static TheoryData ConnectionAdapterData => new TheoryData
{
new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)),
@@ -88,7 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[MemberData(nameof(ConnectionAdapterData))]
public async Task Http10KeepAliveTransferEncoding(ListenOptions listenOptions)
{
- var testContext = new TestServiceContext(LoggerFactory);
+ var testContext = new TestServiceContext();
using (var server = new TestServer(AppChunked, testContext, listenOptions))
{
diff --git a/test/Kestrel.FunctionalTests/DiagnosticMemoryPoolFactory.cs b/test/Kestrel.FunctionalTests/DiagnosticMemoryPoolFactory.cs
new file mode 100644
index 0000000000..e50e1ed305
--- /dev/null
+++ b/test/Kestrel.FunctionalTests/DiagnosticMemoryPoolFactory.cs
@@ -0,0 +1,46 @@
+// 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.Buffers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
+{
+ public class DiagnosticMemoryPoolFactory
+ {
+ private readonly bool _allowLateReturn;
+
+ private readonly bool _rentTracking;
+
+ private readonly List _pools;
+
+ public DiagnosticMemoryPoolFactory(bool allowLateReturn = false, bool rentTracking = false)
+ {
+ _allowLateReturn = allowLateReturn;
+ _rentTracking = rentTracking;
+ _pools = new List();
+ }
+
+ public MemoryPool Create()
+ {
+ lock (_pools)
+ {
+ var pool = new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(),_allowLateReturn, _rentTracking);
+ _pools.Add(pool);
+ return pool;
+ }
+ }
+
+ public Task WhenAllBlocksReturned(TimeSpan span)
+ {
+ lock (_pools)
+ {
+ return Task.WhenAll(_pools.Select(p=>p.WhenAllBlocksReturnedAsync(span)));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs b/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs
index f4b8db2168..de3254e5da 100644
--- a/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs
+++ b/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -90,7 +91,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var clientFinishedSendingRequestBody = new TaskCompletionSource