00:00 | <rbuckton> | Sensitive or not. I may just want the code I'm executing to start fresh, such as ignoring a memoization cache stored in an async context to produce a fresh result. |
00:01 | <rbuckton> | You may not even have access to the async context you need to reset, because its defined in code you don't control. |
00:02 | <rbuckton> | And you don't want to give users the ability to enumerate all async contexts, or change them to arbitrary values. However, most of that code will already have to defensively check whether the context value is undefined , so resetting to a base state isn't a terrible inconvenience. |
00:03 | <littledan> | So we're talking about the scenario where, if you're in the middle you might not have the asynccontext, but the inner code can access the asynccontext of the outer code, right? |
00:03 | <littledan> | And you don't want to give users the ability to enumerate all async contexts, or change them to arbitrary values. However, most of that code will already have to defensively check whether the context value is |
00:03 | <rbuckton> | Yes, exactly. |
00:05 | <rbuckton> | So we're talking about the scenario where, if you're in the middle you might not have the asynccontext, but the inner code can access the asynccontext of the outer code, right? |
00:05 | <littledan> | OK, so you provided an API above with ExecutionContext to achieve this, but if we were to not care about TCP and such, I take it that this could be something like a static method AsyncContext.fresh(cb) |
00:06 | <littledan> | This was the same concern I had about Yehuda's request for some kind of implicit propagation of cancellation tokens several years back. |
00:06 | <rbuckton> | Essentially, yes. I think the TCP issues are still worth discussing in terms of ergonomics for refactoring. |
00:06 | <littledan> | yeah I'm not dismissing them just checking my understanding |
00:06 | <rbuckton> | I have definitely been thinking about how this would apply to cancel tokens and finally fulfill the prophesy! |
00:07 | <littledan> | what is the concern you had in this context? |
00:08 | <rbuckton> | Or at least, if you want implicit propagation you have to roll it yourself with your own `AsyncContext. |
00:10 | <rbuckton> | The "I'm in the middle" concern. Lets say the fetch API had some kind of implicit cancellation. You are a library author whose library has an async function that must execute a fetch to completion (barring network I/O or power interruption issues). If your function is called by an application that just so happens to set this implicit cancellation token for fetch , you have no way to preserve your must execute requirement. |
00:11 | <rbuckton> | This is why cancellation tokens are passed as an argument. If you are sitting in the middle, you can chose whether to forward that argument on to an API based on your function's needs. |
00:11 | <rbuckton> | If that token is in an AsyncContext you don't control, you have no way to preserve your invariant. |
00:12 | <littledan> | right, makes sense |
00:12 | <rbuckton> | If you're only recourse is a blunt object (i.e., AsyncContext.fresh ), you might lose other important context information that the things you are calling still need. |
00:14 | <rbuckton> | So its better just to advise against it. A developer can still do it if they want to, but don't make it any easier than it has to be. |
00:20 | <Justin Ridgewell> | Are we sure we want to allow resetting all contexts, even the ones you don’t have direct access to? |
00:23 | <littledan> | I guess I'm convinced that people should be cautious about when/how to use AsyncContext, but this broader question is unclear to me |
00:42 | <littledan> | for both the vscode and server case, I kinda feel like those systems won't really run into the "in the middle" case, and like they should probably use 1-2 AsyncContexts in the first place |
00:43 | <littledan> | Within a single piece of code, you should only really use multiple AsyncContexts if they are going to differ in extent/nesting from other things, right? You would use a compound data structure within that. (It looks like this is how HTTPContext works, right?) So when you have any kind of restricted-privilege plugin system, you only have a certain set of things to censor |
00:44 | <littledan> | the "in the middle" concern seems like a good reason to not do implicit propagation of cancel tokens, but I don't see how it relates to this case of clearing all contexts |
00:46 | <littledan> | If your only recourse is a blunt object (i.e., |
00:50 | <littledan> | anyway thanks so much for explaining all of this, rbuckton . We don't need to come to a conclusion today, and I'm just happy that I can understand your points on this now. |
00:51 | <littledan> | Overall I'd categorize these things as, even post-Stage 2 things, to resolve before Stage 3. There aren't really any fundamental disagreements about the core semantics of this API, I think. |
01:17 | <rbuckton> | for both the vscode and server case, I kinda feel like those systems won't really run into the "in the middle" case, and like they should probably use 1-2 AsyncContexts in the first place AsyncContext represents a single value, as opposed to a larger mutable store like .NET's ExecutionContext , I would expect you will see far more of them than 1-2 in many applications. |
01:18 | <rbuckton> | Are we sure we want to allow resetting all contexts, even the ones you don’t have direct access to? |
01:19 | <rbuckton> | Sorry where you suggesting a more subtle instrument? I guess I missed that, though I guess the ExecutionContext API put the way to create and set a fresh one less "in your face", which could reduce the risk of accidental usage. |
01:20 | <Kris Kowal> | That’s funny because propagating cancellation tokens is almost certainly the first and most obvious thing this will be used for. |
01:29 | <rbuckton> | I expect the most obvious thing it will be used for is passing along request state in a server. That's what I'm using AsyncLocalStorage for (though in that case, its a Discord bot). |