Move InvokeAsync to ResourceInvoker

This commit is contained in:
Pranav K 2017-01-03 16:22:52 -08:00
parent 6b0282fa84
commit 13b32adeae
2 changed files with 60 additions and 77 deletions

View File

@ -66,43 +66,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
_executor = objectMethodExecutor;
}
public virtual async Task InvokeAsync()
protected override void ReleaseResources()
{
try
if (_controller != null)
{
_diagnosticSource.BeforeAction(
_controllerContext.ActionDescriptor,
_controllerContext.HttpContext,
_controllerContext.RouteData);
using (_logger.ActionScope(_controllerContext.ActionDescriptor))
{
_logger.ExecutingAction(_controllerContext.ActionDescriptor);
var startTimestamp = _logger.IsEnabled(LogLevel.Information) ? Stopwatch.GetTimestamp() : 0;
try
{
await InvokeFilterPipelineAsync();
}
finally
{
if (_controller != null)
{
_controllerFactory.ReleaseController(_controllerContext, _controller);
}
_logger.ExecutedAction(_controllerContext.ActionDescriptor, startTimestamp);
}
}
}
finally
{
_diagnosticSource.AfterAction(
_controllerContext.ActionDescriptor,
_controllerContext.HttpContext,
_controllerContext.RouteData);
_controllerFactory.ReleaseController(_controllerContext, _controller);
}
}
@ -113,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
switch (next)
{
case State.InvokeBegin:
case State.ResourceInsideBegin:
{
goto case State.ExceptionBegin;
}
@ -145,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
else
{
// There are no exception filters - so jump right to 'inside'.
Debug.Assert(scope == Scope.Invoker);
Debug.Assert(scope == Scope.Resource);
goto case State.ActionBegin;
}
}
@ -254,7 +222,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Debug.Assert(state != null);
Debug.Assert(_exceptionContext != null);
if (scope == Scope.Invoker)
if (scope == Scope.Resource)
{
Debug.Assert(_exceptionContext.Result != null);
_result = _exceptionContext.Result;
@ -263,11 +231,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var task = InvokeResultAsync(_exceptionContext.Result);
if (task.Status != TaskStatus.RanToCompletion)
{
next = State.InvokeEnd;
next = State.ResourceInsideEnd;
return task;
}
goto case State.InvokeEnd;
goto case State.ResourceInsideEnd;
}
case State.ExceptionEnd:
@ -491,7 +459,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return TaskCache.CompletedTask;
}
Debug.Assert(scope == Scope.Invoker);
Debug.Assert(scope == Scope.Resource);
goto case State.ResultBegin;
}
@ -678,10 +646,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Rethrow(_resultExecutedContext);
goto case State.InvokeEnd;
goto case State.ResourceInsideEnd;
}
case State.InvokeEnd:
case State.ResourceInsideEnd:
{
isCompleted = true;
return TaskCache.CompletedTask;
@ -899,37 +867,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return _resultExecutedContext;
}
/// <remarks><see cref="ResourceInvoker.InvokeFilterPipelineAsync"/> for details on what the
/// variables in this method represent.</remarks>
protected override async Task InvokeInnerFilterAsync()
{
// The invoker is implemented using a 'Taskerator' or perhaps an 'Asyncerator' (both terms are correct
// and in common usage). This method is the main 'driver' loop and will call into the `Next` method
// (`await`ing the result) until a terminal state is reached.
//
// The `Next` method walks through the state transitions of the invoker and returns a `Task` when there's
// actual async work that we need to await. As an optimization that Next method won't return a `Task`
// that completes synchronously.
//
// Additionally the `Next` funtion will be called recursively when we're 'inside' a filter invocation.
// Executing 'inside' a filter requires an async method call within a `try`/`catch` for error handling, so
// we have to recurse. Each 'frame' calls into `Next` with a value of `Scope` that communicates what kind
// of 'frame' is executing. This has an effect on the state machine transitions as well as what kinds of
// contexts need to be constructed to communicate the result of execution of the 'frame'.
// When returning, the `Next` method will set `next` to the state to goto on the subsequent invocation.
// This is similar to `Task.ContinueWith`, but since we have a fixed number of states we can avoid
// the overhead of actually using `Task.ContinueWith`.
var next = State.InvokeBegin;
// The `scope` tells the `Next` method who the caller is, and what kind of state to initialize to
// communicate a result. The outermost scope is `Scope.Invoker` and doesn't require any type
// of context or result other than throwing.
var scope = Scope.Invoker;
// The `state` is used for internal state handling during transitions between states. In practice this
// means storing a filter instance in `state` and then retrieving it in the next state.
var next = State.ResourceInsideBegin;
var scope = Scope.Resource;
var state = (object)null;
// `isCompleted` will be set to true when we've reached a terminal state.
var isCompleted = false;
while (!isCompleted)
@ -1009,7 +953,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private enum Scope
{
Invoker,
Resource,
Exception,
Action,
Result,
@ -1017,9 +961,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private enum State
{
InvokeBegin,
InvokeBeginOutside,
InvokeBeginInside,
ResourceInsideBegin,
ExceptionBegin,
ExceptionNext,
ExceptionAsyncBegin,
@ -1046,7 +988,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
ResultSyncEnd,
ResultInside,
ResultEnd,
InvokeEnd,
ResourceInsideEnd,
}
}
}

View File

@ -72,7 +72,48 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
_cursor = new FilterCursor(filters);
}
protected async Task InvokeFilterPipelineAsync()
public virtual async Task InvokeAsync()
{
try
{
_diagnosticSource.BeforeAction(
_actionContext.ActionDescriptor,
_actionContext.HttpContext,
_actionContext.RouteData);
using (_logger.ActionScope(_actionContext.ActionDescriptor))
{
_logger.ExecutingAction(_actionContext.ActionDescriptor);
var startTimestamp = _logger.IsEnabled(LogLevel.Information) ? Stopwatch.GetTimestamp() : 0;
try
{
await InvokeFilterPipelineAsync();
}
finally
{
ReleaseResources();
_logger.ExecutedAction(_actionContext.ActionDescriptor, startTimestamp);
}
}
}
finally
{
_diagnosticSource.AfterAction(
_actionContext.ActionDescriptor,
_actionContext.HttpContext,
_actionContext.RouteData);
}
}
/// <summary>
/// In derived types, releases resources such as controller, model, or page instances created as
/// part of invoking the inner pipeline.
/// </summary>
protected abstract void ReleaseResources();
private async Task InvokeFilterPipelineAsync()
{
var next = State.InvokeBegin;