Skip to content

Commit 1c14fb6

Browse files
committed
Remove per connection PipeOptions
- This changes the socket transport to remove the per connection allocation of PipeOptions.
1 parent 2e1063e commit 1c14fb6

File tree

3 files changed

+68
-38
lines changed

3 files changed

+68
-38
lines changed

src/Servers/Kestrel/Transport.Sockets/src/Client/SocketConnectionFactory.cs

+16-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Threading;
1010
using System.Threading.Tasks;
1111
using Microsoft.AspNetCore.Connections;
12-
using Microsoft.AspNetCore.Http.Features;
1312
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
1413
using Microsoft.Extensions.Logging;
1514
using Microsoft.Extensions.Options;
@@ -21,6 +20,8 @@ internal class SocketConnectionFactory : IConnectionFactory, IAsyncDisposable
2120
private readonly SocketTransportOptions _options;
2221
private readonly MemoryPool<byte> _memoryPool;
2322
private readonly SocketsTrace _trace;
23+
private readonly PipeOptions _inputOptions;
24+
private readonly PipeOptions _outputOptions;
2425

2526
public SocketConnectionFactory(IOptions<SocketTransportOptions> options, ILoggerFactory loggerFactory)
2627
{
@@ -38,6 +39,16 @@ public SocketConnectionFactory(IOptions<SocketTransportOptions> options, ILogger
3839
_memoryPool = options.Value.MemoryPoolFactory();
3940
var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Client");
4041
_trace = new SocketsTrace(logger);
42+
43+
var maxReadBufferSize = _options.MaxReadBufferSize ?? 0;
44+
var maxWriteBufferSize = _options.MaxWriteBufferSize ?? 0;
45+
46+
// These are the same, it's either the thread pool or inline
47+
var applicationScheduler = _options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : PipeScheduler.ThreadPool;
48+
var transportScheduler = applicationScheduler;
49+
50+
_inputOptions = new PipeOptions(_memoryPool, applicationScheduler, transportScheduler, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false);
51+
_outputOptions = new PipeOptions(_memoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false);
4152
}
4253

4354
public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endpoint, CancellationToken cancellationToken = default)
@@ -59,12 +70,11 @@ public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endpoint, Cancel
5970
var socketConnection = new SocketConnection(
6071
socket,
6172
_memoryPool,
62-
PipeScheduler.ThreadPool,
73+
_inputOptions.ReaderScheduler, // This is either threadpool or inline
6374
_trace,
64-
_options.MaxReadBufferSize,
65-
_options.MaxWriteBufferSize,
66-
_options.WaitForDataBeforeAllocatingBuffer,
67-
_options.UnsafePreferInlineScheduling);
75+
_inputOptions,
76+
_outputOptions,
77+
_options.WaitForDataBeforeAllocatingBuffer);
6878

6979
socketConnection.Start();
7080
return socketConnection;

src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs

+3-18
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,9 @@ internal SocketConnection(Socket socket,
3636
MemoryPool<byte> memoryPool,
3737
PipeScheduler transportScheduler,
3838
ISocketsTrace trace,
39-
long? maxReadBufferSize = null,
40-
long? maxWriteBufferSize = null,
41-
bool waitForData = true,
42-
bool useInlineSchedulers = false)
39+
PipeOptions inputOptions,
40+
PipeOptions outputOptions,
41+
bool waitForData = true)
4342
{
4443
Debug.Assert(socket != null);
4544
Debug.Assert(memoryPool != null);
@@ -60,23 +59,9 @@ internal SocketConnection(Socket socket,
6059
// https://github.com/aspnet/KestrelHttpServer/issues/2573
6160
var awaiterScheduler = OperatingSystem.IsWindows() ? transportScheduler : PipeScheduler.Inline;
6261

63-
var applicationScheduler = PipeScheduler.ThreadPool;
64-
if (useInlineSchedulers)
65-
{
66-
transportScheduler = PipeScheduler.Inline;
67-
awaiterScheduler = PipeScheduler.Inline;
68-
applicationScheduler = PipeScheduler.Inline;
69-
}
70-
7162
_receiver = new SocketReceiver(_socket, awaiterScheduler);
7263
_sender = new SocketSender(_socket, awaiterScheduler);
7364

74-
maxReadBufferSize ??= 0;
75-
maxWriteBufferSize ??= 0;
76-
77-
var inputOptions = new PipeOptions(MemoryPool, applicationScheduler, transportScheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
78-
var outputOptions = new PipeOptions(MemoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);
79-
8065
var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions);
8166

8267
// Set the transport and connection id

src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs

+49-14
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
1717
internal sealed class SocketConnectionListener : IConnectionListener
1818
{
1919
private readonly MemoryPool<byte> _memoryPool;
20-
private readonly int _numSchedulers;
21-
private readonly PipeScheduler[] _schedulers;
20+
private readonly int _settingsCount;
21+
private readonly Settings[] _settings;
2222
private readonly ISocketsTrace _trace;
2323
private Socket? _listenSocket;
24-
private int _schedulerIndex;
24+
private int _settingsIndex;
2525
private readonly SocketTransportOptions _options;
2626
private SafeSocketHandle? _socketHandle;
2727

@@ -38,21 +38,43 @@ internal SocketConnectionListener(
3838
_memoryPool = _options.MemoryPoolFactory();
3939
var ioQueueCount = options.IOQueueCount;
4040

41+
var maxReadBufferSize = _options.MaxReadBufferSize ?? 0;
42+
var maxWriteBufferSize = _options.MaxWriteBufferSize ?? 0;
43+
var applicationScheduler = options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : PipeScheduler.ThreadPool;
44+
4145
if (ioQueueCount > 0)
4246
{
43-
_numSchedulers = ioQueueCount;
44-
_schedulers = new IOQueue[_numSchedulers];
47+
_settingsCount = ioQueueCount;
48+
_settings = new Settings[_settingsCount];
4549

46-
for (var i = 0; i < _numSchedulers; i++)
50+
for (var i = 0; i < _settingsCount; i++)
4751
{
48-
_schedulers[i] = new IOQueue();
52+
var transportScheduler = options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : new IOQueue();
53+
54+
_settings[i] = new Settings
55+
{
56+
Scheduler = transportScheduler,
57+
InputOptions = new PipeOptions(_memoryPool, applicationScheduler, transportScheduler, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false),
58+
OutputOptions = new PipeOptions(_memoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false)
59+
};
4960
}
5061
}
5162
else
5263
{
53-
var directScheduler = new PipeScheduler[] { PipeScheduler.ThreadPool };
54-
_numSchedulers = directScheduler.Length;
55-
_schedulers = directScheduler;
64+
var transportScheduler = options.UnsafePreferInlineScheduling ? PipeScheduler.Inline : PipeScheduler.ThreadPool;
65+
66+
var directScheduler = new Settings[]
67+
{
68+
new Settings
69+
{
70+
Scheduler = transportScheduler,
71+
InputOptions = new PipeOptions(_memoryPool, applicationScheduler, transportScheduler, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false),
72+
OutputOptions = new PipeOptions(_memoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false)
73+
}
74+
};
75+
76+
_settingsCount = directScheduler.Length;
77+
_settings = directScheduler;
5678
}
5779
}
5880

@@ -127,13 +149,19 @@ void BindSocket()
127149
acceptSocket.NoDelay = _options.NoDelay;
128150
}
129151

130-
var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace,
131-
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer,
132-
_options.UnsafePreferInlineScheduling);
152+
var setting = _settings[_settingsIndex];
153+
154+
var connection = new SocketConnection(acceptSocket,
155+
_memoryPool,
156+
setting.Scheduler,
157+
_trace,
158+
setting.InputOptions,
159+
setting.OutputOptions,
160+
waitForData: _options.WaitForDataBeforeAllocatingBuffer);
133161

134162
connection.Start();
135163

136-
_schedulerIndex = (_schedulerIndex + 1) % _numSchedulers;
164+
_settingsIndex = (_settingsIndex + 1) % _settingsCount;
137165

138166
return connection;
139167
}
@@ -173,5 +201,12 @@ public ValueTask DisposeAsync()
173201
_memoryPool.Dispose();
174202
return default;
175203
}
204+
205+
private class Settings
206+
{
207+
public PipeScheduler Scheduler { get; init; } = default!;
208+
public PipeOptions InputOptions { get; init; } = default!;
209+
public PipeOptions OutputOptions { get; init; } = default!;
210+
}
176211
}
177212
}

0 commit comments

Comments
 (0)