Skip to content

Commit

Permalink
Revert "Revert "Wait to dispose RequestAborted CTS (#4447)"" (#6812)
Browse files Browse the repository at this point in the history
This reverts commit 29b7c5c.
  • Loading branch information
halter73 authored Jan 18, 2019
1 parent 9c017cd commit 7d4ae60
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 21 deletions.
1 change: 1 addition & 0 deletions eng/PatchConfig.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Later on, this will be checked using this condition:
Microsoft.AspNetCore.Mvc.Core;
Microsoft.AspNetCore.Routing;
Microsoft.AspNetCore.Server.IIS;
Microsoft.AspNetCore.Server.Kestrel.Core;
java:signalr;
</PackagesInPatch>
</PropertyGroup>
Expand Down
36 changes: 16 additions & 20 deletions src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,8 @@ public void Reset()
// Lock to prevent CancelRequestAbortedToken from attempting to cancel an disposed CTS.
lock (_abortLock)
{
if (!_requestAborted)
{
_abortedCts?.Dispose();
_abortedCts = null;
}
_abortedCts?.Dispose();
_abortedCts = null;
}

_requestHeadersParsed = 0;
Expand Down Expand Up @@ -415,15 +412,16 @@ protected virtual bool BeginRead(out ValueTask<ReadResult> awaitable)

private void CancelRequestAbortedToken()
{
try
{
_abortedCts.Cancel();
_abortedCts.Dispose();
_abortedCts = null;
}
catch (Exception ex)
lock (_abortLock)
{
Log.ApplicationError(ConnectionId, TraceIdentifier, ex);
try
{
_abortedCts?.Cancel();
}
catch (Exception ex)
{
Log.ApplicationError(ConnectionId, TraceIdentifier, ex);
}
}
}

Expand All @@ -437,12 +435,12 @@ protected void AbortRequest()
}

_requestAborted = true;
}

if (_abortedCts != null)
{
// Potentially calling user code. CancelRequestAbortedToken logs any exceptions.
ServiceContext.Scheduler.Schedule(state => ((HttpProtocol)state).CancelRequestAbortedToken(), this);
if (_abortedCts != null && !_preventRequestAbortedCancellation)
{
// Potentially calling user code. CancelRequestAbortedToken logs any exceptions.
ServiceContext.Scheduler.Schedule(state => ((HttpProtocol)state).CancelRequestAbortedToken(), this);
}
}
}

Expand All @@ -462,8 +460,6 @@ private void PreventRequestAbortedCancellation()
}

_preventRequestAbortedCancellation = true;
_abortedCts?.Dispose();
_abortedCts = null;
}
}

Expand Down
20 changes: 19 additions & 1 deletion src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ public Http1ConnectionTests()
var connectionFeatures = new FeatureCollection();
connectionFeatures.Set(Mock.Of<IConnectionLifetimeFeature>());

_serviceContext = new TestServiceContext();
_serviceContext = new TestServiceContext()
{
Scheduler = PipeScheduler.Inline
};

_timeoutControl = new Mock<ITimeoutControl>();
_http1ConnectionContext = new HttpConnectionContext
{
Expand Down Expand Up @@ -724,6 +728,20 @@ public async Task RequestAbortedTokenIsResetBeforeLastWriteWithChunkedEncoding()
Assert.False(_http1Connection.RequestAborted.IsCancellationRequested);
}

[Fact]
public void RequestAbortedTokenIsFullyUsableAfterCancellation()
{
var originalToken = _http1Connection.RequestAborted;
var originalRegistration = originalToken.Register(() => { });

_http1Connection.Abort(new ConnectionAbortedException());

Assert.True(originalToken.WaitHandle.WaitOne(TestConstants.DefaultTimeout));
Assert.True(_http1Connection.RequestAborted.WaitHandle.WaitOne(TestConstants.DefaultTimeout));

Assert.Equal(originalToken, originalRegistration.Token);
}

[Fact]
public async Task ExceptionDetailNotIncludedWhenLogLevelInformationNotEnabled()
{
Expand Down

2 comments on commit 7d4ae60

@davidfowl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@halter73 no manual reset events

@halter73
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set Scheduler = PipeScheduler.Inline in the test ctor as part of this change to ensure both WaitHandles are already be completed by the time WaitOne is called.

This is verifying the CTS backing RequestAborted token (if there is one) isn't disposed. For a second, we could just verify CancellationTokenRegistration.Token getter didn't throw an ODE, but that's been fixed.

Would you prefer I make the WaitOne timeout 0? Or just call the WaitHandle getter? I'm pretty sure it's the getter that would throw the ODE, not the call to WaitOne anyway. I don't think it's a big deal how we do it since it's already completed.

Please sign in to comment.