Reduce allocations (#6132)
- Remove per request allocations by implementing IThreadPoolWorkItem on the IISHttpContext. - Removed per operation allocations by using UnsafeQueueUserWorkItem in AsyncIOOperation. - This should also reduce overhead by removing non-essential ExecutionContext propagation logic
This commit is contained in:
parent
a7b783724e
commit
dd912850eb
|
|
@ -25,7 +25,7 @@ using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.IIS.Core
|
namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
internal abstract partial class IISHttpContext : NativeRequestContext, IDisposable
|
internal abstract partial class IISHttpContext : NativeRequestContext, IThreadPoolWorkItem, IDisposable
|
||||||
{
|
{
|
||||||
private const int MinAllocBufferSize = 2048;
|
private const int MinAllocBufferSize = 2048;
|
||||||
private const int PauseWriterThreshold = 65536;
|
private const int PauseWriterThreshold = 65536;
|
||||||
|
|
@ -531,5 +531,40 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invoked by the thread pool
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_ = HandleRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleRequest()
|
||||||
|
{
|
||||||
|
bool successfulRequest = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
successfulRequest = await ProcessRequestAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(0, ex, $"Unexpected exception in {nameof(IISHttpContext)}.{nameof(HandleRequest)}.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Post completion after completing the request to resume the state machine
|
||||||
|
PostCompletion(ConvertRequestCompletionResults(successfulRequest));
|
||||||
|
|
||||||
|
Server.DecrementRequests();
|
||||||
|
|
||||||
|
// Dispose the context
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NativeMethods.REQUEST_NOTIFICATION_STATUS ConvertRequestCompletionResults(bool success)
|
||||||
|
{
|
||||||
|
return success ? NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_CONTINUE
|
||||||
|
: NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,21 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
_nativeApplication.Dispose();
|
_nativeApplication.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void IncrementRequests()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _outstandingRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DecrementRequests()
|
||||||
|
{
|
||||||
|
if (Interlocked.Decrement(ref _outstandingRequests) == 0 && Stopping)
|
||||||
|
{
|
||||||
|
// All requests have been drained.
|
||||||
|
_nativeApplication.StopCallsIntoManaged();
|
||||||
|
_shutdownSignal.TrySetResult(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static NativeMethods.REQUEST_NOTIFICATION_STATUS HandleRequest(IntPtr pInProcessHandler, IntPtr pvRequestContext)
|
private static NativeMethods.REQUEST_NOTIFICATION_STATUS HandleRequest(IntPtr pInProcessHandler, IntPtr pvRequestContext)
|
||||||
{
|
{
|
||||||
IISHttpServer server = null;
|
IISHttpServer server = null;
|
||||||
|
|
@ -159,11 +174,12 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
// Unwrap the server so we can create an http context and process the request
|
// Unwrap the server so we can create an http context and process the request
|
||||||
server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
||||||
Interlocked.Increment(ref server._outstandingRequests);
|
server.IncrementRequests();
|
||||||
|
|
||||||
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(state => _ = HandleRequest((IISHttpContext)state), context);
|
ThreadPool.UnsafeQueueUserWorkItem(context, preferLocal: false);
|
||||||
|
|
||||||
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -174,23 +190,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task HandleRequest(IISHttpContext context)
|
|
||||||
{
|
|
||||||
bool successfulRequest = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
successfulRequest = await context.ProcessRequestAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
context.Server._logger.LogError(0, ex, $"Unexpected exception in {nameof(IISHttpServer)}.{nameof(HandleRequest)}.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
CompleteRequest(context, successfulRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool HandleShutdown(IntPtr pvRequestContext)
|
private static bool HandleShutdown(IntPtr pvRequestContext)
|
||||||
{
|
{
|
||||||
IISHttpServer server = null;
|
IISHttpServer server = null;
|
||||||
|
|
@ -238,28 +237,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CompleteRequest(IISHttpContext context, bool result)
|
|
||||||
{
|
|
||||||
// Post completion after completing the request to resume the state machine
|
|
||||||
context.PostCompletion(ConvertRequestCompletionResults(result));
|
|
||||||
|
|
||||||
if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping)
|
|
||||||
{
|
|
||||||
// All requests have been drained.
|
|
||||||
context.Server._nativeApplication.StopCallsIntoManaged();
|
|
||||||
context.Server._shutdownSignal.TrySetResult(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispose the context
|
|
||||||
context.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static NativeMethods.REQUEST_NOTIFICATION_STATUS ConvertRequestCompletionResults(bool success)
|
|
||||||
{
|
|
||||||
return success ? NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_CONTINUE
|
|
||||||
: NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_FINISH_REQUEST;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class IISContextFactory<T> : IISContextFactory
|
private class IISContextFactory<T> : IISContextFactory
|
||||||
{
|
{
|
||||||
private readonly IHttpApplication<T> _application;
|
private readonly IHttpApplication<T> _application;
|
||||||
|
|
|
||||||
|
|
@ -151,10 +151,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO
|
||||||
{
|
{
|
||||||
if (Continuation != null)
|
if (Continuation != null)
|
||||||
{
|
{
|
||||||
// TODO: use generic overload when code moved to be netcoreapp only
|
ThreadPool.UnsafeQueueUserWorkItem(Continuation, State, preferLocal: false);
|
||||||
var continuation = Continuation;
|
|
||||||
var state = State;
|
|
||||||
ThreadPool.QueueUserWorkItem(_ => continuation(state));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue