Bug 1391707: Part 1 - Use idle dispatch in DeferredTask. r?florian
MozReview-Commit-ID: Ktlu71aIcRZ
--- a/devtools/server/performance/profiler.js
+++ b/devtools/server/performance/profiler.js
@@ -382,17 +382,17 @@ const ProfilerManager = (function () {
/**
* Will enable or disable "profiler-status" events depending on
* if there are subscribers and if the profiler is current recording.
*/
_updateProfilerStatusPolling: function () {
if (this._profilerStatusSubscribers > 0 && nsIProfilerModule.IsActive()) {
if (!this._poller) {
this._poller = new DeferredTask(this._emitProfilerStatus.bind(this),
- this._profilerStatusInterval);
+ this._profilerStatusInterval, 0);
}
this._poller.arm();
} else if (this._poller) {
// No subscribers; turn off if it exists.
this._poller.disarm();
}
},
--- a/toolkit/modules/DeferredTask.jsm
+++ b/toolkit/modules/DeferredTask.jsm
@@ -104,20 +104,25 @@ const Timer = Components.Constructor("@m
* not considered complete until that promise resolves. This
* task is never re-entered while running.
* @param aDelayMs
* Time between executions, in milliseconds. Multiple attempts to run
* the task before the delay has passed are coalesced. This time of
* inactivity is guaranteed to pass between multiple executions of the
* task, except on finalization, when the task may restart immediately
* after the previous execution finished.
+ * @param aIdleTimeoutMs
+ * The maximum time to wait for an idle slot on the main thread after
+ * aDelayMs have elapsed. If omitted, waits indefinitely for an idle
+ * callback.
*/
-this.DeferredTask = function(aTaskFn, aDelayMs) {
+this.DeferredTask = function(aTaskFn, aDelayMs, aIdleTimeoutMs) {
this._taskFn = aTaskFn;
this._delayMs = aDelayMs;
+ this._timeoutMs = aIdleTimeoutMs;
if (aTaskFn.isGenerator()) {
Cu.reportError(new Error(`Unexpected generator function passed to DeferredTask`));
}
}
this.DeferredTask.prototype = {
/**
@@ -294,18 +299,24 @@ this.DeferredTask.prototype = {
// Indicate that the execution of the task has finished. This happens
// synchronously with the previous state changes in the function.
this._runningPromise = null;
})().catch(Cu.reportError));
},
/**
- * Executes the associated task and catches exceptions.
+ * Executes the associated task in an idle callback and catches exceptions.
*/
async _runTask() {
try {
- await this._taskFn();
+ // If we're being finalized, execute the task immediately, so we don't
+ // risk blocking async shutdown longer than necessary.
+ if (this._finalized || this._timeoutMs === 0) {
+ await this._taskFn();
+ } else {
+ await PromiseUtils.idleDispatch(this._taskFn, this._timeoutMs);
+ }
} catch (ex) {
Cu.reportError(ex);
}
},
};
--- a/toolkit/modules/JSONFile.jsm
+++ b/toolkit/modules/JSONFile.jsm
@@ -32,17 +32,16 @@ this.EXPORTED_SYMBOLS = [
"JSONFile",
];
// Globals
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
"resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
--- a/toolkit/modules/PromiseUtils.jsm
+++ b/toolkit/modules/PromiseUtils.jsm
@@ -1,28 +1,55 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"
this.EXPORTED_SYMBOLS = ["PromiseUtils"];
+Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/Timer.jsm");
this.PromiseUtils = {
/*
* Creates a new pending Promise and provide methods to resolve and reject this Promise.
*
* @return {Deferred} an object consisting of a pending Promise "promise"
* and methods "resolve" and "reject" to change its state.
*/
defer() {
return new Deferred();
},
+
+ /**
+ * Requests idle dispatch to the main thread for the given callback,
+ * and returns a promise which resolves to the callback's return value
+ * when it has been executed.
+ *
+ * @param {function} callback
+ * @param {integer} [timeout]
+ * An optional timeout, after which the callback will be
+ * executed immediately if idle dispatch has not yet occurred.
+ *
+ * @returns {Promise}
+ */
+ idleDispatch(callback, timeout = 0) {
+ return new Promise((resolve, reject) => {
+ Services.tm.idleDispatchToMainThread(
+ () => {
+ try {
+ resolve(callback());
+ } catch (e) {
+ reject(e);
+ }
+ },
+ timeout);
+ });
+ },
}
/**
* The definition of Deferred object which is returned by PromiseUtils.defer(),
* It contains a Promise and methods to resolve/reject it.
*/
function Deferred() {
/* A method to resolve the associated Promise with the value passed.