Skip to content

Commit

Permalink
Add inline scheduler option for Sockets transport (#24638)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrennanConroy authored Aug 18, 2020
1 parent 512a49c commit c92eaa2
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endpoint, Cancel
_trace,
_options.MaxReadBufferSize,
_options.MaxWriteBufferSize,
_options.WaitForDataBeforeAllocatingBuffer);
_options.WaitForDataBeforeAllocatingBuffer,
_options.UnsafePreferInlineScheduling);

socketConnection.Start();
return socketConnection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ internal sealed class SocketConnection : TransportConnection

internal SocketConnection(Socket socket,
MemoryPool<byte> memoryPool,
PipeScheduler scheduler,
PipeScheduler transportScheduler,
ISocketsTrace trace,
long? maxReadBufferSize = null,
long? maxWriteBufferSize = null,
bool waitForData = true)
bool waitForData = true,
bool useInlineSchedulers = false)
{
Debug.Assert(socket != null);
Debug.Assert(memoryPool != null);
Expand All @@ -60,16 +61,24 @@ internal SocketConnection(Socket socket,
// On *nix platforms, Sockets already dispatches to the ThreadPool.
// Yes, the IOQueues are still used for the PipeSchedulers. This is intentional.
// https://github.com/aspnet/KestrelHttpServer/issues/2573
var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline;
var awaiterScheduler = IsWindows ? transportScheduler : PipeScheduler.Inline;

var applicationScheduler = PipeScheduler.ThreadPool;
if (useInlineSchedulers)
{
transportScheduler = PipeScheduler.Inline;
awaiterScheduler = PipeScheduler.Inline;
applicationScheduler = PipeScheduler.Inline;
}

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

maxReadBufferSize ??= 0;
maxWriteBufferSize ??= 0;

var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, scheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
var outputOptions = new PipeOptions(MemoryPool, scheduler, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);
var inputOptions = new PipeOptions(MemoryPool, applicationScheduler, transportScheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
var outputOptions = new PipeOptions(MemoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ public async ValueTask<ConnectionContext> AcceptAsync(CancellationToken cancella
}

var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace,
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer);
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer,
_options.UnsafePreferInlineScheduling);

connection.Start();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public class SocketTransportOptions

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

/// <summary>
/// Inline application and transport continuations instead of dispatching to the threadpool.
/// </summary>
/// <remarks>
/// This will run application code on the IO thread which is why this is unsafe.
/// 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
/// at the runtime layer as well.
/// This setting can make performance worse if there is expensive work that will end up holding onto the IO thread for longer than needed.
/// Test to make sure this setting helps performance.
/// </remarks>
public bool UnsafePreferInlineScheduling { get; set; }

internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = System.Buffers.SlabMemoryPoolFactory.Create;
}
}

0 comments on commit c92eaa2

Please sign in to comment.