Bug 1388664: Execute _saveStateAsync in idle callback. r?mikedeboer draft
authorBeekill95 <nnn_bikiu0707@yahoo.com>
Fri, 11 Aug 2017 08:49:52 +0700
changeset 648617 8f0036d3f822623c584b856276c1bebcecdcd176
parent 645499 59594b3879cc7b038ba8a1ed6e9e66c6423bf6e2
child 726878 88350d39ac7c05d087d64f5d19292c2bfacfb304
push id74815
push userbmo:nnn_bikiu0707@yahoo.com
push dateFri, 18 Aug 2017 01:57:43 +0000
reviewersmikedeboer
bugs1388664
milestone57.0a1
Bug 1388664: Execute _saveStateAsync in idle callback. r?mikedeboer MozReview-Commit-ID: EmqpUkoUeUi
browser/components/sessionstore/SessionSaver.jsm
browser/components/sessionstore/test/browser_backup_recovery.js
--- a/browser/components/sessionstore/SessionSaver.jsm
+++ b/browser/components/sessionstore/SessionSaver.jsm
@@ -107,16 +107,22 @@ this.SessionSaver = Object.freeze({
 var SessionSaverInternal = {
   /**
    * The timeout ID referencing an active timer for a delayed save. When no
    * save is pending, this is null.
    */
   _timeoutID: null,
 
   /**
+   * The idle callback ID referencing an active idle callback. When no idle
+   * callback is pending, this is null.
+   * */
+  _idleCallbackID: null,
+
+  /**
    * A timestamp that keeps track of when we saved the session last. We will
    * this to determine the correct interval between delayed saves to not deceed
    * the configured session write interval.
    */
   _lastSaveTime: 0,
 
   /**
    * `true` if the user has been idle for at least
@@ -171,33 +177,52 @@ var SessionSaverInternal = {
     }
 
     // Interval until the next disk operation is allowed.
     let interval = this._isIdle ? this._intervalWhileIdle : this._intervalWhileActive;
     delay = Math.max(this._lastSaveTime + interval - Date.now(), delay, 0);
 
     // Schedule a state save.
     this._wasIdle = this._isIdle;
-    this._timeoutID = setTimeout(() => this._saveStateAsync(), delay);
+    this._timeoutID = setTimeout(() => {
+      let hiddenDOMWindow = Services.appShell.hiddenDOMWindow;
+
+      // Execute _saveStateAsync when we have enough idle time. Otherwise,
+      // another idle request is made to schedule _saveStateAsync again.
+      let saveStateAsyncWhenIdle = (deadline) => {
+        // When looking at the telemetry data, the time it takes to execute
+        // _saveStateAsync is around 5.9ms (median). Therefore,
+        // we'll not execute the function when the idle time is less than 5ms.
+        if (deadline.timeRemaining() < 5) {
+          this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
+          return;
+        }
+        this._saveStateAsync();
+      };
+
+      this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
+    }, delay);
   },
 
   /**
    * Sets the last save time to the current time. This will cause us to wait for
    * at least the configured interval when runDelayed() is called next.
    */
   updateLastSaveTime() {
     this._lastSaveTime = Date.now();
   },
 
   /**
    * Cancels all pending session saves.
    */
   cancel() {
     clearTimeout(this._timeoutID);
     this._timeoutID = null;
+    Services.appShell.hiddenDOMWindow.cancelIdleCallback(this._idleCallbackID);
+    this._idleCallbackID = null;
   },
 
   /**
    * Observe idle/ active notifications.
    */
   observe(subject, topic, data) {
     switch (topic) {
       case "idle":
--- a/browser/components/sessionstore/test/browser_backup_recovery.js
+++ b/browser/components/sessionstore/test/browser_backup_recovery.js
@@ -25,16 +25,19 @@ function promiseRead(path) {
 add_task(async function init() {
   // Make sure that we are not racing with SessionSaver's time based
   // saves.
   Services.prefs.setIntPref(PREF_SS_INTERVAL, 10000000);
   registerCleanupFunction(() => Services.prefs.clearUserPref(PREF_SS_INTERVAL));
 });
 
 add_task(async function test_creation() {
+  // Cancel all pending session saves so they won't get in our way.
+  SessionSaver.cancel();
+
   // Create dummy sessionstore backups
   let OLD_BACKUP = Path.join(Constants.Path.profileDir, "sessionstore.baklz4");
   let OLD_UPGRADE_BACKUP = Path.join(Constants.Path.profileDir, "sessionstore.baklz4-0000000");
 
   await File.writeAtomic(OLD_BACKUP, "sessionstore.bak");
   await File.writeAtomic(OLD_UPGRADE_BACKUP, "sessionstore upgrade backup");
 
   await SessionFile.wipe();