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

ExecutionContext.SuppressFlow() suppression state does not flow #16307

Closed
TimLovellSmith opened this issue Feb 9, 2018 · 4 comments
Closed

ExecutionContext.SuppressFlow() suppression state does not flow #16307

TimLovellSmith opened this issue Feb 9, 2018 · 4 comments

Comments

@TimLovellSmith
Copy link

@TimLovellSmith TimLovellSmith commented Feb 9, 2018

Hi,
I have been wrestling with behavior of ExecutionContext.SuppressFlow() when combined with await/async. I have slowly come to believe the current behavior (in .net version I'm using at least) is flawed. Here is the problem.

Because ExecutionContext does not come for free, and saving/restoring it has measurable overheads,
I think it is a good idea for me to suppress the ExecutionContext flow in a method I have which loops for a long time and happens to be implemented as async. I.e. something like this

async void ReadWriteLoop()
{
    ExecutionContext.SuppressFlow();
    while (true)
    {
         await AsyncCallNumberOne();
         await AsyncCallNumberTwo();
         await AsyncCallNumberThree();
    }
}

async Task AsyncCallNumberOne()
{
    await AsyncCallNumberFour();
    await AsyncCallNumberFive();
}

However, the observed behavior of this code is that after each async call, ExecutionContext.IsFlowSuppressed() might return false.
This presents a problem. The only way I can definitely get ExecutionContext to be suppressed for this entire logical execution flow is by peppering suppress calls everywhere:

void EnsureSuppressed() { If (!ExecutionContext.IsFlowSuppressed()) ExecutionContext.SuppressFlow(); }

async void ReadWriteLoop()
{
    while (true)
    {
         EnsureSuppressed();
         await AsyncCallNumberOne();
         EnsureSuppressed();
         await AsyncCallNumberTwo();
         EnsureSuppressed();
         await AsyncCallNumberThree();
    }
}

async Task AsyncCallNumberOne()
{
    Debug.Assert(ExecutionContext.IsFlowSuppressed(), "we know it is suppressed by the caller... in this case");
    await AsyncCallNumberFour();
    EnsureSuppressed();
    await AsyncCallNumberFive();
}

I.e. the observed behavior is that while the execution context successfully is suppressed, and we resume execution without restoring the execution context, the suppression state is not restored, requiring us to suppress execution context all over again in order to get it really suppressed.

This doesn't seem good. Calling ExecutionConext.IsFlowSuppressed() and ExecutionContext.SuppressFlow() like this isn't free in either lines of code nor CPU and memory!

The behavior I actually believe should happen is that the suppression state should be preserved around the async call, meaning there need be no further calls to EnsureSuppressed();

That would better fulfil what I believe the intent of ExecutionContext.SuppressFlow() is: to suppress flow of the entire context for the current 'logical execution operation'.

And then it also might be feasible to undo the suppression correctly (currently it seems like it is a conceptual mess to do this!)

@TimLovellSmith TimLovellSmith changed the title ExecutionContext.SuppressFlow() suppression state does not flow! ExecutionContext.SuppressFlow() suppression state does not flow Feb 9, 2018
@RussKeldorph

This comment has been minimized.

Copy link
Member

@RussKeldorph RussKeldorph commented Feb 9, 2018

@Farham0

This comment has been minimized.

Copy link

@Farham0 Farham0 commented Feb 10, 2019

What do you think of using a delegate?

@TimLovellSmith

This comment has been minimized.

Copy link
Author

@TimLovellSmith TimLovellSmith commented Feb 11, 2019

@Farham0 I don't think I yet understand your question. Are you suggesting I should not use await/async, but use delegates with .ContinueWith(), or Async callbacks approach instead?

@TimLovellSmith

This comment has been minimized.

Copy link
Author

@TimLovellSmith TimLovellSmith commented Jan 10, 2020

Closing this as I'm no longer of the opinion it makes sense. The main problem with my idea is that ExecutionContext is basically a "thread-local" concept, whereas async/await switches you across threads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.