Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/aot/runtime/AOTModuleEvaluation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3138,10 +3138,12 @@ void Modules::evaluateSynchronously(ModuleID module, SynchronousEvaluationReason
promise = m_synchronousLoadsHandedToLoader[handed].promise.get();
m_synchronousLoadsHandedToLoader.removeAt(handed);
SetForScope isAskingHost(m_isAskingHost, true);
SetForScope isLoadingForRequire(m_isLoadingForRequire, true);
JSModuleLoader::drainSynchronousModuleQueue(realm);
} else if (needsLoader) {
takeUpWaysOfLoads();
SetForScope isAskingHost(m_isAskingHost, true);
SetForScope isLoadingForRequire(m_isLoadingForRequire, true);
promise = loader()->loadModuleSync(realm, moduleKey(module), nullptr, nullptr);
} else if (statusAtStart == ModuleStatus::Unlinked && moduleData.reachesUncompiledCode()) {
ModuleRecord* record = registerWithLoader(module);
Comment thread
robobun marked this conversation as resolved.
Resolved
Expand Down
30 changes: 29 additions & 1 deletion Source/JavaScriptCore/aot/runtime/AOTModules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,13 @@ bool Modules::hasRunToItsEndWithItsCycle(const ModuleRecord& record) const
// An ES module of the program that has run needs no record, like one that ran with the graph of another: whoever asks for it again is handed a record that says so.
void Modules::retireRecordsOfSettledLoads()
{
if (m_instance.vm->synchronousModuleQueue() || m_innermostEvaluation || m_rootWhoseFilesRun || m_rootWhoseFilesRanAhead || m_fileWhoseJobsWaitForLoader || !m_levelsOfLoads.isEmpty())
// A frame on the stack holds each of these two, and ends with no load that settles: a require() returns, a body goes on behind the jobs that it had the host run. Nobody would ask again.
if (m_instance.vm->synchronousModuleQueue() || m_innermostEvaluation) {
if (!m_modulesWhoseRecordsLoaderHolds.isEmpty()) [[unlikely]]
Comment thread
robobun marked this conversation as resolved.
Resolved
retireRecordsWhenVMIsIdle();
return;
}
if (m_rootWhoseFilesRun || m_rootWhoseFilesRanAhead || m_fileWhoseJobsWaitForLoader || !m_levelsOfLoads.isEmpty())
return;
// The host loads the graph of the entry point.
if (!m_firstModuleRequestedByHost || status(*m_firstModuleRequestedByHost) < ModuleStatus::Evaluating)
Expand Down Expand Up @@ -821,6 +827,28 @@ void Modules::retireRecordsOfSettledLoads()
}
}

NEVER_INLINE void Modules::retireRecordsWhenVMIsIdle()
{
if (m_isLoadingForRequire || m_retiresRecordsWhenVMIsIdle)
return;
m_retiresRecordsWhenVMIsIdle = true;
// The collector may take the realm of this image before the VM is idle. It takes the instance out of the list then.
VM& vm = *m_instance.vm;
vm.whenIdle([&vm] {
Comment thread
robobun marked this conversation as resolved.
Resolved
VMState& state = vm.aotState();
Locker locker { state.instancesLock };
for (Instance* instance : state.instances) {
Modules* modules = instance->state ? instance->state->modules.get() : nullptr;
if (!modules || !modules->m_retiresRecordsWhenVMIsIdle)
continue;
RunningImageScope imageOfInstance(state, instance->program);
modules->retireRecordsOfSettledLoads();
// Where nothing has entered the VM, VM::whenIdle() has called at once, under what refused: that call is refused too, and finds this set.
modules->m_retiresRecordsWhenVMIsIdle = false;
}
});
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
void didSettleLoad(JSPromise* settled)
{
JSGlobalObject* realm = settled->realm();
Expand Down
5 changes: 5 additions & 0 deletions Source/JavaScriptCore/aot/runtime/AOTModules.h
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ class Modules {
ModuleRecord* ensureRecordWithoutStatus(ModuleID);
void retireRecord(ModuleRecord*);
void retireRecordOfModuleThatHasNotBegun(ModuleRecord*);
void retireRecordsWhenVMIsIdle();
bool hasRunToItsEndWithItsCycle(const ModuleRecord&) const;
void setValue(ModuleID, JSValue);
LoadedModule& ensureLoaded(ModuleID);
Expand Down Expand Up @@ -1078,6 +1079,10 @@ class Modules {
BitVector m_filesWhoseOriginalRuns;
bool m_hasAskedEveryClass { false };
bool m_isWalkingRequests { false };
// A require() that asks the loader calls retireRecordsOfSettledLoads() behind its load: what is refused in that load waits for the call.
bool m_isLoadingForRequire { false };
// retireRecordsOfSettledLoads() was refused by what is on the stack, and is called again when the VM is idle.
bool m_retiresRecordsWhenVMIsIdle { false };
struct LoadInFlight {
Strong<JSPromise> promise;
ModuleID root;
Expand Down
Loading
Loading