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 valuev
, it behaves as if it were a function returningv
. - 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 |
|
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. |