Skip to content

Commit c92eaa2

Browse files
Add inline scheduler option for Sockets transport (#24638)
1 parent 512a49c commit c92eaa2

File tree

4 files changed

+30
-7
lines changed

4 files changed

+30
-7
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endpoint, Cancel
6363
_trace,
6464
_options.MaxReadBufferSize,
6565
_options.MaxWriteBufferSize,
66-
_options.WaitForDataBeforeAllocatingBuffer);
66+
_options.WaitForDataBeforeAllocatingBuffer,
67+
_options.UnsafePreferInlineScheduling);
6768

6869
socketConnection.Start();
6970
return socketConnection;

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ internal sealed class SocketConnection : TransportConnection
3737

3838
internal SocketConnection(Socket socket,
3939
MemoryPool<byte> memoryPool,
40-
PipeScheduler scheduler,
40+
PipeScheduler transportScheduler,
4141
ISocketsTrace trace,
4242
long? maxReadBufferSize = null,
4343
long? maxWriteBufferSize = null,
44-
bool waitForData = true)
44+
bool waitForData = true,
45+
bool useInlineSchedulers = false)
4546
{
4647
Debug.Assert(socket != null);
4748
Debug.Assert(memoryPool != null);
@@ -60,16 +61,24 @@ internal SocketConnection(Socket socket,
6061
// On *nix platforms, Sockets already dispatches to the ThreadPool.
6162
// Yes, the IOQueues are still used for the PipeSchedulers. This is intentional.
6263
// https://github.com/aspnet/KestrelHttpServer/issues/2573
63-
var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline;
64+
var awaiterScheduler = IsWindows ? transportScheduler : PipeScheduler.Inline;
65+
66+
var applicationScheduler = PipeScheduler.ThreadPool;
67+
if (useInlineSchedulers)
68+
{
69+
transportScheduler = PipeScheduler.Inline;
70+
awaiterScheduler = PipeScheduler.Inline;
71+
applicationScheduler = PipeScheduler.Inline;
72+
}
6473

6574
_receiver = new SocketReceiver(_socket, awaiterScheduler);
6675
_sender = new SocketSender(_socket, awaiterScheduler);
6776

6877
maxReadBufferSize ??= 0;
6978
maxWriteBufferSize ??= 0;
7079

71-
var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, scheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
72-
var outputOptions = new PipeOptions(MemoryPool, scheduler, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);
80+
var inputOptions = new PipeOptions(MemoryPool, applicationScheduler, transportScheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
81+
var outputOptions = new PipeOptions(MemoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);
7382

7483
var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions);
7584

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ public async ValueTask<ConnectionContext> AcceptAsync(CancellationToken cancella
124124
}
125125

126126
var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace,
127-
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer);
127+
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer,
128+
_options.UnsafePreferInlineScheduling);
128129

129130
connection.Start();
130131

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ public class SocketTransportOptions
4444

4545
public long? MaxWriteBufferSize { get; set; } = 64 * 1024;
4646

47+
/// <summary>
48+
/// Inline application and transport continuations instead of dispatching to the threadpool.
49+
/// </summary>
50+
/// <remarks>
51+
/// This will run application code on the IO thread which is why this is unsafe.
52+
/// It is recommended to set the DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS environment variable to '1' when using this setting to also inline the completions
53+
/// at the runtime layer as well.
54+
/// This setting can make performance worse if there is expensive work that will end up holding onto the IO thread for longer than needed.
55+
/// Test to make sure this setting helps performance.
56+
/// </remarks>
57+
public bool UnsafePreferInlineScheduling { get; set; }
58+
4759
internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = System.Buffers.SlabMemoryPoolFactory.Create;
4860
}
4961
}

0 commit comments

Comments
 (0)