Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 2e80a204eb24e3950edd84a43ad42fe8aa8ba693
https://github.com/WebKit/WebKit/commit/2e80a204eb24e3950edd84a43ad42fe8aa8ba693
Author: Mark Lam <[email protected]>
Date: 2025-10-05 (Sun, 05 Oct 2025)
Changed paths:
A Source/JavaScriptCore/API/tests/VMManagerStopTheWorldTest.cpp
A Source/JavaScriptCore/API/tests/VMManagerStopTheWorldTest.h
M Source/JavaScriptCore/API/tests/testapi.c
M Source/JavaScriptCore/CMakeLists.txt
M Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
M Source/JavaScriptCore/heap/MarkedBlock.cpp
M Source/JavaScriptCore/llint/LowLevelInterpreter.asm
M Source/JavaScriptCore/runtime/JSCConfig.h
A Source/JavaScriptCore/runtime/StopTheWorldCallback.h
M Source/JavaScriptCore/runtime/VM.cpp
M Source/JavaScriptCore/runtime/VM.h
M Source/JavaScriptCore/runtime/VMManager.cpp
M Source/JavaScriptCore/runtime/VMManager.h
A Source/JavaScriptCore/runtime/VMThreadContext.h
M Source/JavaScriptCore/runtime/VMTraps.cpp
M Source/JavaScriptCore/runtime/VMTraps.h
M Source/JavaScriptCore/runtime/VMTrapsInlines.h
M Source/JavaScriptCore/shell/CMakeLists.txt
M Source/JavaScriptCore/wasm/WasmCallee.cpp
Log Message:
-----------
Implement a Stop the World mechanism for JSC.
https://bugs.webkit.org/show_bug.cgi?id=300046
rdar://161844101
Reviewed by Yijia Huang.
This is needed by Global GC, the Wasm Debugger, and the Memory Debugger that
we're exploring
implementing.
The following is documented in VMManager.h, but is replicated here for your
convenience in
reading and discovery:
Understanding Stop the World (STW)
==================================
Intuition on how to think about things?
=======================================
The actors in play for a Stop the World story are:
1. VMManager
2. VM
3. a Conductor Agent
Events / actions involved in the Stop the World story are:
1. Stop requests (with a given StopReason)
2. Stop callback handlers
An intuitive way to think about the Stop the World story is:
1. VMManager is an abstraction representing the process. There is only 1
singleton
VMManager instance, and it coordinates the tracking and scheduling of
VMs.
2. VM (and its VMThreadContext) represents a thread. A VM instance may
actually
run on different machine threads at different times (JSC's API allows
this).
However, from VMManager's perspective, each VM is like a thread that can
be
suspended / stopped, and resumed.
FIXME: the current VMManager does NOT yet handle cases where more than
one VM
is run on the same machine thread e.g. one VM1 calls into C++, which in
turn
calls into VM2. VM2 now has control of the CPU, but VMManager does not
know
that VM1 is in a way "deactivated". This scenario cannot manifest in
WebKit
with web workloads though.
3. The Conductor Agent is something like a Debugger agent that tells
VMManager
to stop or resume VMs / threads.
4. Stop requests are like interrupts. A stop being requested is analogous
to an
interrupt firing.
5. Stop callback handlers are like interrupt handlers that masked out all
interrupts
so that no other interrupts can fire while the current one is being
handled.
When a stop request occurs, VMManager::notifyVMStop() will dispatch a
callback
to the appropriate handler for that request. Similar to the interrupt
analogy, only one request can be serviced at one time. All other requests
regardless of priority will be blocked (and held in pending) until the
current
request is done being serviced.
World Execution Modes
=====================
The VMManager has a notion of a world mode (see VMManager::Mode). These
modes are:
1. RunAll - all threads can run or are running.
2. RunOne - only one thread can run, like when a debugger is single
stepping.
3. Stopping - a stop has been requested, and VMs are in the process of
stopping.
4. Stopped - all threads have been stopped, and the highest priority stop
request
can now be serviced.
Querying VMManager Info
=======================
VMManager::info() provides a view into a few pieces of VMManager state:
1. numberOfVMs - the number of VMs that have been constructed and are alive.
2. numberOfActiveVMs - the number of VMs that are activated i.e. their
threads
have entered the VM. This numberOfActiveVMs is only available while
the
world is NOT in Mode::RunAll (i.e. must be in some form of stoppage).
3. numberOfStoppedVMs - the number of VMs that have reached the stopping
point
in VMManager::notifyVMStop(). The currently executing targetVM is
counted
as stopped when single stepping in Mode::RunOne.
3. worldMode - this is the current VM world mode (as described above).
Currently, this info is mainly used for testing purposes only.
Initiating Stop the World
=========================
Stop the World begins with some agent calling VMManager::requestStopAll()
with a
StopReason. This agent can be from mutator threads or from a helper thread
like
those employed by debuggers.
More than one agent can request STW at the same time. Hence, there can be
multiple
stop requests queued up while the world is being stopped.
StopReason and their Priority
=============================
Current StopReasons are:
None - no requests
GC - requesting stop for Global GC
WasmDebugger - requesting stop for Wasm Debugger (like Ctrl-C in lldb)
MemoryDebugger - similar to WasmDebugger, but for the Memory Debugger.
The priority of these requests are defined by their order of declaration.
See definition of FOR_EACH_STOP_THE_WORLD_REASON and enum class StopReason.
StopReason::None is a special case and has no priority.
StopReason::GC is the current highest priority request.
StopReason::MemoryDebugger is the current lowest priority request.
StopReason is synonymous with "StopRequest".
From the client's perspective, it is the reason for a stop request.
From the VMManager's perspective, it is the type of stop request.
Servicing Order: one request at a time
======================================
The order the stop requests came in does not matter. Once the world is
finally
stopped, the higher priority request is serviced first.
See fetchTopPriorityStopReason() and m_currentStopReason.
While this request is being serviced, other requests will be ignored. During
this time of service, new stop requests can be added to
m_pendingStopRequestBits,
but they will be ignored even if they are higher priority. We will service
them
only after the current request has resumed with RunOne or RunAll mode.
StopTheWorldCallback (i.e. stop request handlers)
=================================================
When the world is stopped, VMManager will call back to a request handler
based on what StopReason is in m_currentStopReason. See
VMManager::notifyVMStop().
The handler for GC should be static but is not currently implemented yet.
The handlers for WasmDebugger and MemoryDebugger may be overridden. They are
made to be overrideable only to enable testing. See
VMManagerStopTheWorldTest.cpp
for how they are used in testing.
Each handler must be of the shape StopTheWorldCallback. See
StopTheWorldCallback.h
The handler will be called with a StopTheWorldEvent. The StopTheWorldEvent
indicates
where the handler is called from. This may have a use later on, but for
now, the
StopTheWorldEvent is only informational.
After the handler is done, it controls how execution will proceed
thereafter by
returning one of the StopTheWorldCallback return values (see
StopTheWorldCallback.h).
The possible return values are:
1. STW_CONTINUE()
- this is only used for testing purposes where we want to loop inside
VMManager::notifyVMStop() while waiting for more things to handle.
- VMManager::m_worldMode will remain in Mode::Stopped.
2. STW_CONTEXT_SWITCH(targetVM)
- this is used to switch control of the handler to another VM on a
different
thread without resuming any execution. lldb's "thread select ..." can
be
implemented this way.
- VMManager::m_worldMode will remain in Mode::Stopped.
3. STW_RESUME_ONE()
- this is used to resume only the current VM thread in RunOne mode. This
is
useful for debuggers that wish to single step in the current VM. It
keeps
other threads paused / stopped while this thread executes. It is up to
the
client to detect potential resource deadlocks (e.g. using a timeout)
that
may arise from only resuming 1 thread.
- VMManager::m_worldMode will transition from Mode::Stopped to
Mode::RunOne.
4. STW_RESUME_ALL()
- forces all threads to resume from a stop.
- VMManager::m_worldMode will transition from Mode::Stopped to
Mode::RunAll.
5. STW_RESUME()
- Return to whatever run mode we were executing with before the current
Stop
the World request. That may be either Mode::RunOne or Mode::RunAll.
- This allows the GC to run (with its own Stop the World requests) even
while
while we're single stepping in a debugger with Mode::RunOne.
Edge Cases and Special Circumstances
====================================
While in Mode::RunOne, if the VM that is running either exits the VM (aka
deactivates) or its VM is destructed (aka shutdown), the VMManager will
transition
the world mode back to RunAll (unblocking other VMs and threads) since the
current
VM is no longer viable for continuing execution.
Tests: Source/JavaScriptCore/API/tests/VMManagerStopTheWorldTest.cpp
Source/JavaScriptCore/API/tests/VMManagerStopTheWorldTest.h
Source/JavaScriptCore/API/tests/testapi.c
* Source/JavaScriptCore/API/tests/VMManagerStopTheWorldTest.cpp: Added.
(VMManagerStopTheWorldTest::abortTest):
(VMManagerStopTheWorldTest::threadsList):
(VMManagerStopTheWorldTest::checkpointCallback):
(VMManagerStopTheWorldTest::ensureAliveCallback):
(VMManagerStopTheWorldTest::wasmDebuggerTestCallback):
(VMManagerStopTheWorldTest::memoryDebuggerTestCallback):
(VMManagerStopTheWorldTest::notifyVMDestruction):
(VMManagerStopTheWorldTest::WTF_REQUIRES_LOCK):
(VMManagerStopTheWorldTest::test):
* Source/JavaScriptCore/API/tests/VMManagerStopTheWorldTest.h: Added.
* Source/JavaScriptCore/API/tests/testapi.c:
(main):
* Source/JavaScriptCore/CMakeLists.txt:
* Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj:
* Source/JavaScriptCore/heap/MarkedBlock.cpp:
(JSC::MarkedBlock::dumpInfoAndCrashForInvalidHandleV2):
* Source/JavaScriptCore/llint/LowLevelInterpreter.asm:
* Source/JavaScriptCore/runtime/JSCConfig.h:
* Source/JavaScriptCore/runtime/StopTheWorldCallback.h: Added.
* Source/JavaScriptCore/runtime/VM.cpp:
(JSC::VM::checkStaticAsserts):
(JSC::VM::VM):
(JSC::VM::~VM):
(JSC::VM::hasExceptionsAfterHandlingTraps):
(JSC::VM::throwTerminationException):
(JSC::VM::updateStackLimits):
(JSC::VM::executeEntryScopeServicesOnEntry):
(JSC::VM::executeEntryScopeServicesOnExit):
* Source/JavaScriptCore/runtime/VM.h:
(JSC::VM::hasEntryScopeServiceRequest):
(JSC::VM::offsetOfTraps):
(JSC::VM::fromThreadContext):
(JSC::VM::threadContext):
(JSC::VM::clearLastException):
(JSC::VM::softStackLimit const):
(JSC::VM::addressOfSoftStackLimit):
(JSC::VM::cloopStack):
(JSC::VM::cloopStack const):
(JSC::VM::cloopStackLimit):
(JSC::VM::currentCLoopStackPointer const):
(JSC::VM::traps):
(JSC::VM::traps const):
(JSC::VM::notifyNeedDebuggerBreak):
(JSC::VM::notifyNeedShellTimeoutCheck):
(JSC::VM::notifyNeedTermination):
(JSC::VM::notifyNeedWatchdogCheck):
(JSC::VM::requestStop):
(JSC::VM::cancelStop):
* Source/JavaScriptCore/runtime/VMManager.cpp:
(JSC::VMManager::singleton):
(JSC::VMThreadContext::VMThreadContext):
(JSC::VMThreadContext::~VMThreadContext):
(JSC::WTF_REQUIRES_LOCK):
(JSC::VMManager::findMatchingVMImpl):
(JSC::VMManager::forEachVMImpl):
(JSC::VMManager::forEachVMWithTimeoutImpl):
(JSC::VMManager::info):
(JSC::VMManager::setWasmDebuggerCallback):
(JSC::VMManager::setMemoryDebuggerCallback):
(JSC::VMManager::requestStopAllInternal):
(JSC::VMManager::requestResumeAllInternal):
(JSC::VMManager::notifyVMStop):
(JSC::VMManager::notifyVMConstruction):
(JSC::VMManager::notifyVMDestruction):
(JSC::VMManager::notifyVMActivation):
(JSC::VMManager::notifyVMDeactivation):
(JSC::VMManager::handleVMDestructionWhileWorldStopped):
(JSC::VMManager::add): Deleted.
(JSC::VMManager::remove): Deleted.
* Source/JavaScriptCore/runtime/VMManager.h:
(JSC::VMManager::numberOfVMs):
(JSC::VMManager::requestStopAll):
(JSC::VMManager::requestResumeAll):
(JSC::VMManager::findMatchingVM):
(JSC::VMManager::forEachVM):
(JSC::VMManager::forEachVMWithTimeout):
(JSC::VMManager::hasPendingStopRequests const):
* Source/JavaScriptCore/runtime/VMThreadContext.h: Added.
(JSC::VMThreadContext::next const):
(JSC::VMThreadContext::traps):
(JSC::VMThreadContext::traps const):
(JSC::VMThreadContext::offsetOfTraps):
* Source/JavaScriptCore/runtime/VMTraps.cpp:
(JSC::VMTraps::handleTraps):
* Source/JavaScriptCore/runtime/VMTraps.h:
* Source/JavaScriptCore/runtime/VMTrapsInlines.h:
(JSC::VMTraps::vm const):
* Source/JavaScriptCore/shell/CMakeLists.txt:
* Source/JavaScriptCore/wasm/WasmCallee.cpp:
(JSC::Wasm::Callee::reportToVMsForDestruction):
Canonical link: https://commits.webkit.org/301027@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes