# Browser Shutdown, how does it work? #

## General Overview
- The parent process goes through a number of observer notifications that
  trigger different phases of shutdown.  Traditionally, each observer would spin
  its own nested event loop until it was satisfied shutdown had been completed,
  resulting in serialized shutdown with a lot of potential inefficiency.  We're
  now moving to using the AsyncShutdown mechanism which tells all its consumers
  about a phase in parallel and then spins a single event loop until they all
  report shutdown completion.  If they don't do so in a timely fashion, a crash
  is induced and the crash reporter indicates what shutdown blockers were still
  outstanding, making it easier to figure out who to yell at.
  - JS consumers should consult https://searchfox.org/mozilla-central/source/toolkit/components/asyncshutdown/AsyncShutdown.jsm
  - C++ consumers should consult https://searchfox.org/mozilla-central/source/toolkit/components/asyncshutdown/nsIAsyncShutdown.idl
- For Content Processes, most observed shutdown behavior will happen because
  when PContent is torn down this will destroy all the PBrowser instances, which
  will in turn destroy the windows/documents.  Prior to this, some very minimal
  observer notifications will run that are designed for when cleanup is
  essential and can't be handled like the process crashed.  After  this, things
  diverge based on build type:  
  - Under release builds, both the parent and the child start timers to
    terminate the child, currently set to 5 seconds.  But unless the process is
    badly unresponsive, this is unlikely to matter because
    ContentChild::ActorDestroy will immediately terminate the process via
    exit(0).  This will happen after all of its child actors are destroyed.
  - Under debug builds, there are no termination timers and there is no exit(0),
    and since PBackground is still alive, most PBackground services will see
    an orderly shutdown of their clients.  The full set of observer
    notifications will also

## IPC Details
This just deals with parent/child stuff.  There's a gazillion shutdown handlers
in the parent.

### Content Process Shutdown

#### ContentParent
- each ContentParent::Observe receives "profile-before-change" and:
  - Sends a "Shutdown" message to its ContentChild via
    `ShutDownProcess(SEND_SHUTDOWN_MESSAGE);` then spins a nested event loop
    until IPC has closed or our timeout mechanism caused us to forcibly kill the
    child.  The loop looks like:
    `SpinEventLoopUntil([&]() { return !mIPCOpen || mCalledKillHard; });`
    - ShutDownProcess will also invoke StartForceKillTimer() which creates a
      timer that waits "dom.ipc.tabs.shutdownTimeoutSecs" seconds before
      invoking ContentParent::KillHard().  Complications:
      - In the presence of an XPCSHELL_TEST_PROFILE_DIR env var that suggests
        xpcshell tests, KillHard never gets invoked no matter what.
      - Under debug builds, the pref is set to 0 which causes the timer to not
        be initialized.  This helps verify clean shutdown because there are
        still other shutdown timers in play that will trigger an abort.
      - Under non-(debug/ASAN/TSAN/Valgrind) builds, the value is set to 5
        seconds, which is also the in-code default.
  - While in that loop:
    - A "FinishShutdown" message will be received.  This will invoke
      `ShutDownProcess(CLOSE_CHANNEL)` which will:
      - QuotaManagerService::AbortOperationsForProcess(mChildID) will be
        invoked.


#### ContentChild
The below happens for each child.
- ContentChild receives a "Shutdown" message.
  - A "content-child-will-shutdown" observer notification is generated.  This
    was added in https://bugzilla.mozilla.org/show_bug.cgi?id=1425559 to surface
    shutdown to the new nsThreadManager::SpinEventLoopUntilOrShutdown(condition)
    method so that modal dialogs and other event-loop spinners can know to
    terminate at shutdown.
    - Note that ContentChild::IsShuttingDown() also exists if you're thinking
      about observing for this notification, but that it won't return true until
      all nested event loops have been popped.
  - CrashReporter::AnnotateCrashReport logs the "IPCShutdownState" as
    RecvShutdown.
  - ContentChild::ShutdownInternal() is invoked which covers the rest of the
    logic.
  - If a nested event loop is detected (they won't have unwound yet), a 100ms
    backoff will repeatedly be tried before advancing.  It's ShutdownInternal
    that gets rescheduled, so the previous steps won't get repeated.
  - A "content-child-shutdown" observer notification is generated.  (Contrast
    with the earlier WILL shutdown message.)
    - TelemtrySession.jsm does Telemetry.flushBatchedChildTelemetry().
    - ContentObservers.js removes its WebRTC observers that would otherwise be
      relayed to ContentWebRTC.jsm, presumably with some kind of overhead.
  - On XP mozilla::widget::StopAudioSession() is invoked for some reason.
  - The PContent IPC channel is set to not abort on error.  (Where abort means
    crash the process.)
  - The profiler is told to send any profiling results and shutdown if it was
    compiled in and active.
  - The child force kill timer is activated.  This uses the same
    "dom.ipc.tabs.shutdownTimeoutSecs" pref as the parent, but without the
    xpcshell carve-out.  The pref is 0, disabling the kill timer, on non-debug
    builds.
  - A "FinishShutdown" message is sent to the parent.
- The PContent channel will be closed by the parent, resulting in PContent being
  torn down and ContentChild::ActorDestroy being invoked which will:
  - If in a release build, ProcessChild::QuickExit() which is equivalent to
    exit(0) will be invoked, self-terminating the process with a success error
    code.  Stop reading now, nothing more will ever happen in this case.
  - Various small cleanup stuff happens:
    - The ConsoleListener that propagates console message to the parent via
      PContent "ScriptErrorWithStack" or "ScriptError" messages is removed.
  - XRE_ShutdownChildProcess is invoked which:
    - The Scheduler is shutdown.  This is the thing that maintains a bunch of
      main-thread priority queues and existed for the now doomed cooperative
      scheduling effort.
    - MessageLoop::Quit() is invoked which will set the quite_received flag so
      that the next time MessageLoop::DoIdleWork is invoked and does not have
      anything to do, it will invoke MessagePump::Quit.  For the main thread,
      DoIdleWork will only be invoked by MessagePump::Run once
      NS_ProcessNextEvent(, aMayWait=false) returns false indicating that there
      were no events processed.  This means that we wait for the normal XPCOM
      main-thread event target to become empty, plus the IPC and IPC idle queues
      to drain as well.  Once that happens, keep_running_ gets set and
      MessagePump::Run will be allowed to terminate.  (And this must happen
      after the ActorDestroy call returns because it's mechanisms above this on
      the stack.)
- Once MessagePump::Run terminates, we return this this callstack:
  - MessagePumpForChildProcess::Run
  - XRE_InitChildProcess which will invoke ContentProcess::Cleanup which invokes
    XRE_TermEmbedding which does:
    - nsXREDirProvider::DoShutdown which does all the observer notifications,
      see https://searchfox.org/mozilla-central/rev/c0d81882c7941c4ff13a50603e37095cdab0d1ea/toolkit/xre/nsXREDirProvider.cpp#1059
      noting that this is guarded by mProfileNotified which only triggers and
      induces startup notifications when preferences are received from the
      parent process.
      - "profile-change-net-teardown"
      - "profile-change-teardown"
      - "profile-before-change"
      - "profile-before-change-qm"
      - "profile-before-change-telemetry"
    - NS_ShutdownXPCOM is invoked which invokes mozilla::ShutdownXPCOM which is
      a general shutdown that's identical to what happens in the parent, but I
      went and started transcribing stuff below because I love observer
      notifications:
      https://searchfox.org/mozilla-central/source/xpcom/build/XPCOMInit.cpp#801
      - sends observer notifications:
        - "xpcom-will-shutdown"
        - "xpcom-shutdown"
        - "xpcom-shutdown-threads"
      - marks threads as shutting down
      - nsTimerImpl::Shutdown()
      - actual thread shutdown via nsThreadManager::Shutdown()
      - "xpcom-shutdown-loaders" observers are saved off
      - the observer service is shutdown
      - mozilla::services::Shutdown() is invoked
      - nsComponentManagerImpl::FreeServices()
      - "xpcom-shutdown-loaders" is manually dispatched from the saved-off
        enumerator.
      - nsCycleCollector_shutdown(shutdownCollect); where shutdownCollect is
        true if DEBUG/TSAN/ASAN/etc. or run if the "MOZ_CC_RUN_DURING_SHUTDOWN"
        env var is present under release/non-debug.
      - nsComponentManagerImpl::Shutdown() is invoked
      - JS_ShutDown()
      - NSS_Shutdown()
      - TODO: finish analysis/put this generic bit someplace more sensible.
  - content_process_main
