Clear [[AsyncEvaluation]] when a module finishes evaluating - #1646
Open
andreasrosdal wants to merge 2 commits into
Open
Clear [[AsyncEvaluation]] when a module finishes evaluating#1646andreasrosdal wants to merge 2 commits into
andreasrosdal wants to merge 2 commits into
Conversation
AsyncModuleExecutionFulfilled clears module->async_evaluation, but the two
other paths that move a module to JS_MODULE_STATUS_EVALUATED do not:
js_set_module_evaluated(), which finishes every module drained from the
available-ancestors list, and js_async_module_execution_rejected(). Both
leave the flag set on a module that is finished, which is what
[[AsyncEvaluationOrder]] = done exists to prevent.
A later evaluation then treats the finished module as still pending.
js_inner_module_evaluation() checks m1->async_evaluation to decide whether
to register a dependency, so it bumps pending_async_dependencies and
appends itself to the finished module's async_parent_modules. Nothing will
ever drain that list again, so the new module never executes and its
top-level promise never settles:
// a.mjs
await 0;
export const x = 1;
// b.mjs
import { x } from './a.mjs';
export const y = x + 1;
// c.mjs
import { y } from './b.mjs';
export const z = y + 1;
// main.mjs
const b = await import('./b.mjs'); // b.y = 2
console.log('b.y =', b.y);
const c = await import('./c.mjs'); // hangs, never resolves
console.log('c.z =', c.z);
b is evaluated through the available-ancestors path, so its flag stays set
after a's top-level await resolves; importing c afterwards deadlocks. V8
prints both lines.
Clear the flag in both places. Fixes test262
language/module-code/top-level-await/module-graphs-does-not-hang.js, which
is listed in test262_errors.txt; drop the entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mv33YvfHz7t9mmkituBnn
The stale flag is only observable from a *later* module: importing a finished module that still looks asynchronous registers the importer as an async parent of a module that will never notify its parents again, so the importer's evaluation promise never settles. Settling is decided against a bounded chain of microtask turns instead of a timer, so the test fails deterministically on the wedged case rather than letting the process exit quietly with the promise still pending. Both the fulfilled and the rejected path are covered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6eRbuuuCujKgQkrHgvMrc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
AsyncModuleExecutionFulfilledclearsmodule->async_evaluation, but the two other paths that move a module toJS_MODULE_STATUS_EVALUATEDdo not:js_set_module_evaluated(), which finishes every module drained from the available-ancestors list, andjs_async_module_execution_rejected(). Both leave the flag set on a module that is finished, which is exactly what[[AsyncEvaluationOrder]] = doneexists to prevent.A later evaluation then treats the finished module as still pending.
js_inner_module_evaluation()checksm1->async_evaluationto decide whether to register a dependency, so it bumpspending_async_dependenciesand appends itself to the finished module'sasync_parent_modules. Nothing will ever drain that list again, so the new module never executes and its top-level promise never settles:bis evaluated through the available-ancestors path, so its flag stays set aftera's top-level await resolves; importingcafterwards deadlocks. V8 prints both lines.Clear the flag in both places. Fixes
test262/test/language/module-code/top-level-await/module-graphs-does-not-hang.js, which is listed intest262_errors.txt; the entry is dropped.🤖 Generated with Claude Code
https://claude.ai/code/session_014mv33YvfHz7t9mmkituBnn
Generated by Claude Code