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

Reply via email to