Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ExecutionContext.Restore rather than EC.Run callback #37942

Merged
merged 3 commits into from
Jul 6, 2020

Conversation

benaadams
Copy link
Member

@benaadams benaadams commented Jun 16, 2020

Only running inline needs to do extra work and error handling for post run restore

@stephentoub this would be the change for ManualResetValueTaskSourceCore to use ExecutionContext.Restore rather than a generic ref passing callback based ExecutionContext.Run

@stephentoub
Copy link
Member

Thanks, @benaadams. Any perf impact?

@benaadams benaadams force-pushed the ExecutionContext.Restore branch from 11f08f2 to a212991 Compare June 17, 2020 04:09
@benaadams
Copy link
Member Author

benaadams commented Jun 17, 2020

Moved to using same api as would use externally.

Only issue/question is undoing SynchronizationContext changes for running inline #37942 (comment)

Resolved

@benaadams
Copy link
Member Author

Any perf impact?

For the common path (with no EC) can only see how it would get a little faster since the calls are inlined for that path.

If InvokeContinuationInline was inline (is an uncommon EC path) rather than a separate method it might cause an issue due to bringing the try/catch block into the method.

@benaadams benaadams requested a review from stephentoub June 17, 2020 14:58
@benaadams benaadams force-pushed the ExecutionContext.Restore branch from 4f96c49 to b6b9436 Compare June 18, 2020 08:01
@benaadams
Copy link
Member Author

Rebased

@benaadams benaadams force-pushed the ExecutionContext.Restore branch 2 times, most recently from 36b7a5f to 2c0c15c Compare June 20, 2020 10:07
@benaadams benaadams requested a review from stephentoub June 20, 2020 11:49
Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

LGTM other than my one comment. @kouvel, any concerns?


ExecutionContext? currentContext = ExecutionContext.Capture();
// Restore the captured ExecutionContext before executing anything.
ExecutionContext.Restore(_executionContext);
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to call this method Apply or something similar instead of Restore given how it's used?

Copy link
Member Author

Choose a reason for hiding this comment

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

Depends on results of api review #38011

Copy link
Member

Choose a reason for hiding this comment

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

Why would the ASP.NET use case of the public API not use EC.Run() instead? In any case even for a public API (which would be a somewhat advanced API that may be misused), Apply seems more appropriate to me, maybe something to consider in the API review

Copy link
Member

Choose a reason for hiding this comment

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

Why would the ASP.NET use case of the public API not use EC.Run() instead?

Because EC.Run forces you into a callback model, which is problematic if the code you're wrapping needs to be async.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm ok. Would a RunAsync instead be workable?

Copy link
Member

Choose a reason for hiding this comment

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

It's not just the shape of the method: it's the overhead. A goal here is to avoid the extra machinery/allocation associated with another level of async method / state machine.

Copy link
Member

@kouvel kouvel Jun 24, 2020

Choose a reason for hiding this comment

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

Makes sense, but if that's why we're exposing the API (for perf), then would it make sense to prefix the API name with Unsafe or something else similarly since it feels like it could be easily misused?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm ok. Would a RunAsync instead be workable?

Well just putting it behind a regular async method gives that effect.

The ASP.NET case is inlining all the awaits into a top level request loop (as its allocated once for all requests on the connection); however the loop calls user code, if any of the methods are not async (Task.CompletedTask returning), but also change AsyncLocal<T>s then they are not undone and those changes leak into the next request (dotnet/aspnetcore#15384, dotnet/aspnetcore#13991)

The .Restore method allows the EC to be captured as a hoisted var outside the loop; and then reset back at the end of each loop iteration so each new request starts on a fresh ExecutionContext throwing away any changes from the last iteration dotnet/aspnetcore#23175 (kinda like the ThreadPool does)

Copy link
Member Author

Choose a reason for hiding this comment

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

e.g.

var ec = ExecutionContext.Capture();
while (!cancelled)
{
    await NextRequest();

    await OnStartAsync(); // User code

    await ProcessRequestAsync(); // User code

    await OnCompleteAsync(); // User code

    ExecutionContext.Restore(ec);
}

@benaadams benaadams requested a review from kouvel June 24, 2020 21:22
@benaadams
Copy link
Member Author

Rebased to retrigger tests

@benaadams
Copy link
Member Author

Good to merge?

@kouvel kouvel merged commit 682141e into dotnet:master Jul 6, 2020
@kouvel
Copy link
Member

kouvel commented Jul 6, 2020

Thanks!

@benaadams benaadams deleted the ExecutionContext.Restore branch July 7, 2020 11:49
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants