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

API Proposal: ExecutionContext.Restore(ExecutionContext? executionContext) #38011

Closed
benaadams opened this issue Jun 17, 2020 · 11 comments · Fixed by #40322
Closed

API Proposal: ExecutionContext.Restore(ExecutionContext? executionContext) #38011

benaadams opened this issue Jun 17, 2020 · 11 comments · Fixed by #40322
Labels
api-approved API was approved in API review, it can be implemented area-System.Threading
Milestone

Comments

@benaadams
Copy link
Member

benaadams commented Jun 17, 2020

Background and Motivation

Alternative to the approved api #30867 "ExecutionContext.Run ref overloads"

awaiting a Task returning method that is itself not async does not guarantee undoing AsyncLocal/ExecutionContext changes (as it is the responsibility of the callee)

ExecutionContext does provide an callback based method that acts as a stack:

public sealed class ExecutionContext
{
    ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, object? state)
}

However using it to just undo changes is clunky (involves rearranging code into a callback); as well as applying the current context to run on, when its already on the current context.

It also doesn't provide a return value; and if it was guarding multiple awaits arranging to return a single Task (via using state as a reference) becomes complicated.

Rather than using it in a stack based "apply=>undo" manner; it would be useful if it could be restored directly inline.

Related ASP.NET issues dotnet/aspnetcore#15384, dotnet/aspnetcore#13991

Proposed API

namespace System.Threading
{
    public sealed class ExecutionContext
    {
        public static void Restore(ExecutionContext? executionContext);
    }
}

Usage Examples

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

    await OnStartAsync();

    await ProcessRequestAsync();

    await OnCompleteAsync();

    ExecutionContext.Restore(ec);
}

Alternative Designs

#30867 "ExecutionContext.Run ref overloads"; this again requires contorting the code to the callback based pattern and is problematic for multiple awaits as regular .Run as well as introducing a large method that has generic code expansion.

Also guard the multiple awaits behind an explicitly async method:

while (!cancelled)
{
    await NextRequest();

    await ProcessUserCode();
}

async ValueTask ProcessUserCode()
{
    await OnStartAsync();

    await ProcessRequestAsync();

    await OnCompleteAsync();
}

However this introduces an extra statemachine per request and an additional allocation per request; as well as causing the ExecutionContext to be captured and restored per request; when really we just want to capture it at the start of request processing and only restore/reset it per request.

/cc @davidfowl @stephentoub

@benaadams benaadams added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jun 17, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Jun 17, 2020
@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't figure out the best area label to add to this issue. Please help me learn by adding exactly one area label.

@benaadams
Copy link
Member Author

Related change in ASP.NET that would make use of this api dotnet/aspnetcore#23175

@benaadams
Copy link
Member Author

@stephentoub are you happy for this to be for review?

@stephentoub stephentoub added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Jul 7, 2020
@stephentoub
Copy link
Member

@kouvel, any concerns with this being exposed?

@benaadams
Copy link
Member Author

Was a naming question raised #37942 (comment)

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

@stephentoub
Copy link
Member

Either seems fine to me.

@kouvel
Copy link
Member

kouvel commented Jul 7, 2020

Looks fine to me. I was also wondering if it should be prefixed with Unsafe since it would allow changing the EC arbitrarily which may not have been possible to do before, but can determine in the review.

@terrajobst
Copy link
Member

@stephentoub @kouvel This is marked as 6.0. If we were to approve this this week, would it still make sense for 5.0?

@benaadams
Copy link
Member Author

This is marked as 6.0. If we were to approve this this week, would it still make sense for 5.0?

Would mean the upstream change in aspnet core could be merged for 5.0 dotnet/aspnetcore#23175

/cc @davidfowl

@stephentoub
Copy link
Member

If we were to approve this this week, would it still make sense for 5.0?

Yes

@terrajobst terrajobst added blocking Marks issues that we want to fast track in order to unblock other important work api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation blocking Marks issues that we want to fast track in order to unblock other important work labels Jul 29, 2020
@terrajobst
Copy link
Member

Looks good as proposed.

namespace System.Threading
{
    public sealed class ExecutionContext
    {
        public static void Restore(ExecutionContext? executionContext);
    }
}

@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
tonyredondo referenced this issue in DataDog/dd-trace-dotnet Dec 18, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Threading
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants