AsyncShutdown.jsm

Managing safe shutdown of asynchronous services.

Firefox shutdown is composed of phases that take place sequentially. Typically, each shutdown phase removes some capabilities from the application. For instance, at the end of phase profileBeforeChange, no service is permitted to write to the profile directory (with the exception of Telemetry). Consequently, if any service has requested I/O to the profile directory before or during phase profileBeforeChange, the system must be informed that these requests need to be completed before the end of phase profileBeforeChange. Failing to inform the system of this requirement can (and has been known to) cause data loss.

Example

At some point during shutdown, the Add-On Manager needs to ensure that all add-ons have safely written their data to disk, before writing its own data. Since the data is saved to the profile, this must be completed during phase profileBeforeChange.

AsyncShutdown.profileBeforeChange.addBlocker(
  "Add-on manager: shutting down",
  function condition() {
    let promise = ... // Do whatever must be done before the end of phase profileBeforeChange
    return promise;
  },
  function info() {
    let status = ... // Collect any information that may be useful for debugging shutdown issues with this blocker
    return status;
  }
});


In this example, at some point during phase profileBeforeChange, function condition will be called. Phase profileBeforeChange is guaranteed to not terminate until promise is either resolved or rejected. If, for some reason, promise is never resolved/rejected, Firefox will crash during shutdown to avoid blocking system shutdown, preventing the user from restarting Firefox or burning through a battery. In this case, a crash report is produced, with information on all the shutdown blockers that have not been resolved, and all the additional debug information returned by calls to info().

Instances of Phase

Note that you cannot create an instance of Phase yourself. See Attributes for the instances of Phase. If you need a new Phase added to AsyncShutdown, please file a bug.

Methods overview

void addBlocker(string name, function|promise condition, optional function info)
boolean removeBlocker(function|promise condition)

Methods

addBlocker ()

Register a blocker for the completion of a phase.

void addBlocker(
  in string name,
  in function|promise|* condition,
  optional in function info
)

Arguments
name
The human-readable name of the blocker. Used for debugging/error reporting. Please make sure that the name respects the following model: "Some Service: some action in progress" - for instance "OS.File: flushing all pending I/O".
condition
A condition blocking the completion of the phase. Generally, this is a function returning a promise. This function is evaluated during the phase and the phase is guaranteed to not terminate until the resulting promise is either resolved or rejected. If condition is not a function but another value v, it behaves as if it were a function returning v.
info
Optionally, a function returning information about the current state of the blocker as an object. Used for providing more details when logging errors or crashing.

removeBlocker ()

Remove the blocker for a condition.

If several blockers have been registered for the same condition, remove all these blockers. If no blocker has been registered for this condition, this is a noop.

boolean removeBlocker(
  in function|promise|* condition
)
Arguments
condition
A condition blocking the completion of the phase. This must be the same condition as was passed to addBlocker prior.
Return Value

Returns true if a blocker has been removed, or false otherwise. Note that a result of false may mean either that the blocker has never been installed or that the phase has completed and the blocker has already been resolved.

Examples

AsyncShutdown.profileBeforeChange.addBlocker("Module: just a promise",
     promise); // profileBeforeChange will not complete until
               // promise is resolved or rejected

AsyncShutdown.profileBeforeChange.addBlocker("Module: a callback",
    function condition() {
      // ...
      // Execute this code during profileBeforeChange
      return promise;
      // profileBeforeChange will not complete until promise
      // is resolved or rejected
});

AsyncShutdown.profileBeforeChange.addBlocker("Module: trivial callback",
    function condition() {
      // ...
      // Execute this code during profileBeforeChange
      // No specific guarantee about completion of profileBeforeChange
});

If the promise returned by condition is not resolved/rejected within one minute, the process will crash to avoid blocking system shutdown, preventing the user from restarting Firefox or burning through battery. This is by design. It is the caller's responsibility to ensure that the promise is eventually resolved/rejected.

Properties

Attribute Type Description

profileBeforeChange Read only

Phase

The profile is about to be unmounted. This phase represents the last chance to write data to the profile directory.

sendTelemetry Read only Phase

Telemetry is being sent. This phase represents the last chance for Telemetry to write data.

webWorkersShutdown Read only Phase

JavaScript threads are about to be killed. This phase represents the last chance to communicate with a JavaScript worker, in particular with OS.File.