Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Flatten exception handling #2313

Merged
merged 1 commit into from
Feb 23, 2018
Merged

Conversation

benaadams
Copy link
Contributor

@benaadams benaadams commented Feb 11, 2018

3 nested try blocks with 3 finallys (4 total) and 7 catches in same function O_o

Simplifies the request processing loop function by separating it from the connection cleanup, and reducing the blocks to a single try around the user code call.

@muratg
Copy link
Contributor

muratg commented Feb 13, 2018

@benaadams is this more of an experiment/wip?

@benaadams
Copy link
Contributor Author

benaadams commented Feb 14, 2018

is this more of an experiment/wip?

Not really; its a proper PR 😄

Currently ProcessRequestsAsync creates for the main loop statemachine il that gets an impressive 7 trys deep

.try
{
  .try
  {
    .try
    {
      .try
      {
        .try
        {
          .try
          {
            .try
            {

It also generates a chunky statemachine struct to lift the values across the exception handling and extra stuff in the method; each object reference requiring a GC memory barrier assign.

image

This PR extracts the main loop into a different method, that only deals with the loop; away from the extra processing that only happens once per connection; also simplifies the exception handling in the loop.

After this change the statemachine for the main loop is only gets two trys deep

.try
{
  .try
  {

And the statemachine looses 6 wrap fields (3 object refs); though gains one Exception field; so the state machine struct is reduced by 28 bytes per await and 2 GC memory barriers.

image

Also the main process requests loop is has more clarity of focus and is easier to understand (and 28 lines smaller) as it is only dealing with processing requests in a loop; rather than additionally handling connection cleanup and failure (which is handled in the outer method)

@muratg
Copy link
Contributor

muratg commented Feb 14, 2018

@benaadams thanks that clarifies quite a bit!

@muratg muratg added this to the 2.1.0-preview2 milestone Feb 14, 2018
@davidfowl
Copy link
Member

davidfowl commented Feb 15, 2018

I wouldn't merge this unless we verified that adding another state machine doesn't regress the performance. We had big issues with the original Http2 refactoring doing that.

@benaadams
Copy link
Contributor Author

that adding another state machine doesn't regress the performance

Second statemachine is created once and executed once per connection, was paying close attention 😄
Is why my second commit puts ConsumeAsync back inline as that introduced a third statemachine that could be created and executed multiple times per connection.

@davidfowl
Copy link
Member

I see, it's ProcessRequests(), I thought this was per request but it's still per connection. We'll do a sanity test run.

@davidfowl
Copy link
Member

@benaadams can you rebase?

3 nested try blocks with 3 finallies in same function O_o
@benaadams
Copy link
Contributor Author

Done

try
{
// Finish reading the request body in case the app did not.
await messageBody.ConsumeAsync();
Copy link
Member

Choose a reason for hiding this comment

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

How confident are we that this is the only thing that can throw after invoking the application and verifying the response body length?

Personally, I'm fairly confident this is the case, but I want to make extra sure that we dispose the HttpContext in all the common scenarios.

Copy link
Contributor Author

@benaadams benaadams Feb 17, 2018

Choose a reason for hiding this comment

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

Was thinking FireOnStarting and FireOnCompleted if the user code they executed threw; but they don't let exceptions escape; so should be ok?

Copy link
Member

Choose a reason for hiding this comment

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

Yep. I think this change is safe. I just wanted an extra pair of eyes.

Copy link
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

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

LGTM. I'd like @davidfowl and @Tratcher to also look at this carefully before merging though.

@muratg
Copy link
Contributor

muratg commented Feb 17, 2018

Thanks @halter73!

SetBadRequestState(badRequestException);
}

application.DisposeContext(httpContext, _applicationException);
Copy link
Member

Choose a reason for hiding this comment

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

This not being in a finally makes me nervous, @halter73 I'm hoping our tests are complete here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only thing would be if await ProduceEnd could throw a BadRequestException

Copy link
Member

Choose a reason for hiding this comment

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

Seems like it could? What happens if you set a utf8 response header before the response starts then you throw from the application?

Copy link
Contributor Author

@benaadams benaadams Feb 21, 2018

Choose a reason for hiding this comment

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

They are checked when they are set and would throw then in the application. When they are written out they use PipelineExtensions.WriteAsciiNoValidation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Taking of which EncodeAsciiCharsToBytes looks like it could go faster :)

Copy link
Member

Choose a reason for hiding this comment

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

I don't think ProduceEnd should ever throw.

}
}
}
await ProcessRequests(application);
}
catch (BadHttpRequestException ex)
Copy link
Member

Choose a reason for hiding this comment

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

Can this still happen? At this level all error are connection oriented right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Parsing headers and request line can throw a BadHttpRequestException which gets caught here

Copy link
Member

Choose a reason for hiding this comment

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

I see

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is before any request has started so it catches here and tears down the connection

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants