Task: avoid double handler installation #101
Merged
+11
−9
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Explanation of the suspected issue:
(This suspected issue was noticed by @clef-men; he convinced me that this looks credible and we wrote the proposed fix together.)
The Task implementation seems to be slightly wasteful in that, if we understand correctly, it may install its effect handler twice. This occurs in the following scenario:
asyncawaitthis promiseawaitgives control back, twostephandlers are on the stackTwo handlers are on the stack because:
when the current task is paused by
await, the continuationkcontains the deep handler of the surroundingstepcall:invoking this Work function will thus reinstate the surrounding deep handler.
when this continuation is received by another worker,
stepis called again, installing a second handlerAt this point, if we understand correctly, there are two
stephandlers on the call stack. Note that this does not grow to an unbounded number of nested handlers: on the next Wait effect, the inner handler either continues immediately (still 2 handlers) or pushes the current continuation to a Pending queue and returns, popping the two handlers. Once this continuation is invoked again understep, we are back to 2 handlers.Note that in some case the current implementation does need the
stepcall inworker, because the function argument ofWork fdoes not systematically include a handler:Explanation of our proposed fix:
This PR proposes to fix the issue by enforcing the invariant that the Work function always includes its own handler for Task effects: for the Work functions that use
continueanddiscontinuethere is nothing to do, for the Work function ofasyncwe callstepat this point.Another approach would be to use a shallow handler, and use
stepin the same way as currently (around each Work function), but this may be slightly slower -- we would be exactly encoding a deep handler using a shallow handler. (The current code reads like the authors had the shallow-handler semantics in mind.)Co-authored-by: Clement Allain [email protected]