Skip to content

Add IMemoryPoolFactory and cleanup memory pool while idle #61554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jun 9, 2025
Prev Previous commit
Next Next commit
namedpipe
  • Loading branch information
BrennanConroy committed Mar 20, 2025
commit f17e3eb1cda620fb59a19bf40ec0dfd02170fd71
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public NamedPipeConnectionListener(
_log = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes");
_endpoint = endpoint;
_options = options;
_memoryPool = options.MemoryPoolFactory();
_memoryPool = options.MemoryPoolFactory.CreatePool();
_listeningToken = _listeningTokenSource.Token;
// Have to create the pool here (instead of DI) because the pool is specific to an endpoint.
_poolPolicy = new NamedPipeServerStreamPoolPolicy(endpoint, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.cs" Link="Internal\TransportConnection.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.Generated.cs" Link="Internal\TransportConnection.Generated.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.FeatureCollection.cs" Link="Internal\TransportConnection.FeatureCollection.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\DefaultMemoryPoolFactory.cs" Link="Internal\DefaultMemoryPoolFactory.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.IO.Pipes;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Internal;

namespace Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes;

Expand Down Expand Up @@ -116,5 +117,5 @@ public static NamedPipeServerStream CreateDefaultNamedPipeServerStream(CreateNam
}
}

internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = () => PinnedBlockMemoryPoolFactory.Create();
internal IMemoryPoolFactory MemoryPoolFactory { get; set; } = DefaultMemoryPoolFactory.Instance;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Runtime.Versioning;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes;
using Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.Internal;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -33,7 +34,15 @@ public static IWebHostBuilder UseNamedPipes(this IWebHostBuilder hostBuilder)
{
services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.AddSingleton<IConnectionListenerFactory, NamedPipeTransportFactory>();

services.TryAddSingleton<IMemoryPoolFactory, DefaultMemoryPoolFactory>();
services.AddOptions<NamedPipeTransportOptions>().Configure((NamedPipeTransportOptions options, IMemoryPoolFactory factory) =>
{
// Set the IMemoryPoolFactory from DI on NamedPipeTransportOptions. Usually this should be the PinnedBlockMemoryPoolFactory from UseKestrelCore.
options.MemoryPoolFactory = factory;
});
});

return hostBuilder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.cs" Link="Internal\TransportConnection.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.Generated.cs" Link="Internal\TransportConnection.Generated.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.FeatureCollection.cs" Link="Internal\TransportConnection.FeatureCollection.cs" />
<Compile Include="$(KestrelSharedSourceRoot)\DefaultMemoryPoolFactory.cs" Link="Internal\DefaultMemoryPoolFactory.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal;

namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Net;
using System.Net.Sockets;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal;

namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

Expand Down Expand Up @@ -32,6 +32,7 @@ public static IWebHostBuilder UseSockets(this IWebHostBuilder hostBuilder)
services.TryAddSingleton<IMemoryPoolFactory, DefaultMemoryPoolFactory>();
services.AddOptions<SocketTransportOptions>().Configure((SocketTransportOptions options, IMemoryPoolFactory factory) =>
{
// Set the IMemoryPoolFactory from DI on SocketTransportOptions. Usually this should be the PinnedBlockMemoryPoolFactory from UseKestrelCore.
options.MemoryPoolFactory = factory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we're changing the type of the MemoryPoolFactory from a Func to an IMemoryPoolFactory meaning we're breaking reflection anyway, why flow the factory through the options instead of constructor injecting it in SocketTransportFactory? Just to minimize churn? I guess this also ensures anyone constructing it themselves with an options instance resolved from DI gets the appropriate IMemoryPoolFactory.

I think it's fine to leave it, but I think it's our last chance not to flow the factory through options if we don't want to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would mean new API (since the type is public) or using the internal impl trick and switching to that. We'd need to do it on NamedPipes as well.

});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Buffers;
using Microsoft.AspNetCore.Connections;

namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Internal;

internal sealed class DefaultMemoryPoolFactory : IMemoryPoolFactory
{
Expand Down