Skip to content

Commit 880eb80

Browse files
committed
fix proposal c
1 parent cd491ea commit 880eb80

File tree

1 file changed

+59
-15
lines changed

1 file changed

+59
-15
lines changed

README.md

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ function* gen() {
134134

135135
`.run(val, fn)` creates a new function body. The new function environment is not
136136
equivalent to the outer environment and can not trivially share code fragments
137-
between them. Additionally, `break`/`continue`/`return` can not be
138-
refactored naively.
137+
between them. Additionally, `break`/`continue`/`return` can not be refactored
138+
naively.
139139

140140
It would be more intuitive to be able to insert a single line of code to scope
141141
the `computeResult` and `computeResult2` calls with a new AsyncContext value
@@ -278,19 +278,58 @@ not used with `DisposableStack` would have to be added, because
278278

279279
As a mitigation to the above issue of manually entering and exiting a scope out
280280
of sync with the lexical scoping, an alternative proposal is to specialize the
281-
handling of async context variables in `using` declarations as follows:
282-
283-
- In the `using` machinery, before calling the `@@enter` method, a global flag
284-
would be set that indicates that the `@@enter` method is being called from a
285-
`using` declaration.
286-
- The `@@enter` method implementation would check if the global flag is set. If
287-
it is not, an error would be thrown. If it is set, the `@@enter` method would
288-
capture the async context snapshot and set it into a global variable that is
289-
later read from the `using` machinery. The `@@enter` method would then create
290-
a new async context scope with the new value, and enter it.
291-
- In the `using` machinery, after the `@@enter` method returns, the global flag
292-
would be cleared, and the value of the value of the global snapshot variable
293-
would be captured.
281+
handling of async context variables in `using` declarations as illustrated by
282+
this pseudo code:
283+
284+
```js
285+
const GLOBAL_STACK = [];
286+
287+
function UsingEnter(state, enterMethod) {
288+
const current = CaptureSnapshot();
289+
GLOBAL_STACK.push(current);
290+
try {
291+
enterMethod();
292+
} finally {
293+
const newSnapshot = GLOBAL_STACK.pop();
294+
RestoreSnapshot(newSnapshot);
295+
state.snapshot = current;
296+
}
297+
}
298+
299+
function UsingExit(state, disposeMethod) {
300+
try {
301+
disposeMethod();
302+
} finally {
303+
RestoreSnapshot(state.snapshot);
304+
}
305+
}
306+
307+
function AsyncVariableSymbolEnter(variable, value) {
308+
const current = GLOBAL_STACK.pop();
309+
if (current === undefined) {
310+
throw new TypeError(
311+
"Can not enter an async variable outside of `using`.",
312+
);
313+
}
314+
const newSnapshot = AddToSnapshot(
315+
current,
316+
variable,
317+
value,
318+
);
319+
GLOBAL_STACK.push(newSnapshot);
320+
}
321+
```
322+
323+
- In the `using` machinery, before calling the `@@enter` method, capture the
324+
current snapshot and push it into a global stack variable.
325+
- The `@@enter` method implementation would check if the global stack contains a
326+
snapshot. If it does not, an error would be thrown. If it does, the `@@enter`
327+
method would create a new snapshot from the top most snapshot in the stack by
328+
adding the variable, and then set it back into the same slot in the stack.
329+
This is later read from the `using` machinery.
330+
- In the `using` machinery, after the `@@enter` method returns, the snapshot is
331+
removed from the global stack and entered. The original captured current
332+
snapshot is saved.
294333

295334
- In the `using` machinery, once the `using` declaration lexical scope closes,
296335
as usual the `@@dispose` method is called. Once the `@@dispose` method
@@ -303,6 +342,11 @@ declaration. Binding to a lexical scope is enforced, just like with `run`.
303342

304343
The proposal does add some additional complexity to the `using` machinery.
305344

345+
> With this proposal, `Symbol.enter` on the `AsyncContext.Variable#withValue`
346+
> object would never directly enter a scope, but would instead schedule the
347+
> enter for when the `Symbol.enter` callback is invoked. This is necessary to
348+
> ensure that the scope with the entered value can not leak out.
349+
306350
## Proposal C: enforced disposal through a specialized class
307351

308352
As an alternative mitigation to the above issue of manually entering and exiting

0 commit comments

Comments
 (0)