Bug 959130 - Stop using the SessionWorker for read at startup to avoid slowdown. r=ttaubert, a=lsblakk
authorSteven MacLeod <smacleod@mozilla.com>
Mon, 24 Feb 2014 10:28:21 -0500
changeset 176368 8d35f3f8b28bc40a6134562333361a192969bbde
parent 176367 a185d5633183629c4a3c9f465e5221f8d6d4aad9
child 176369 85053eae528baa69e7445e2fb79a1a998bfbc21a
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersttaubert, lsblakk
bugs959130
milestone28.0
Bug 959130 - Stop using the SessionWorker for read at startup to avoid slowdown. r=ttaubert, a=lsblakk
browser/components/sessionstore/src/SessionFile.jsm
browser/components/sessionstore/src/SessionWorker.js
--- a/browser/components/sessionstore/src/SessionFile.jsm
+++ b/browser/components/sessionstore/src/SessionFile.jsm
@@ -23,23 +23,26 @@ this.EXPORTED_SYMBOLS = ["SessionFile"];
  *   another attempts to copy that file.
  *
  * This implementation uses OS.File, which guarantees property 1.
  */
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
   "@mozilla.org/base/telemetry;1", "nsITelemetry");
 
@@ -142,20 +145,67 @@ let SessionFileInternal = {
   _latestWrite: null,
 
   /**
    * |true| once we have decided to stop receiving write instructiosn
    */
   _isClosed: false,
 
   read: function () {
-    return SessionWorker.post("read").then(msg => {
-      this._recordTelemetry(msg.telemetry);
-      return msg.ok;
+    // We must initialize the worker during startup so it will be ready to
+    // perform the final write. If shutdown happens soon after startup and
+    // the worker has not started yet we may not write.
+    // See Bug 964531.
+    SessionWorker.post("init");
+
+    return Task.spawn(function () {
+      let data = null;
+      for (let filename of [this.path, this.backupPath]) {
+        try {
+          data = yield this._readSessionFile(filename);
+          break;
+        } catch (ex if ex == Cr.NS_ERROR_FILE_NOT_FOUND) {
+          // Ignore exceptions about non-existent files.
+        }
+      }
+
+      throw new Task.Result(data || "");
+    }.bind(this));
+  },
+
+  /**
+   * Read the session file asynchronously.
+   *
+   * @param filename
+   *        string The name of the session file.
+   * @returns {promise}
+   */
+  _readSessionFile: function (path) {
+    let deferred = Promise.defer();
+    let file = FileUtils.File(path);
+    let durationMs = Date.now();
+
+    NetUtil.asyncFetch(file, function(inputStream, status) {
+      if (!Components.isSuccessCode(status)) {
+        deferred.reject(status);
+        return;
+      }
+
+      let byteLength = inputStream.available();
+      let data = NetUtil.readInputStreamToString(inputStream, byteLength,
+        { charset: "UTF-8" });
+      durationMs = Date.now() - durationMs;
+
+      deferred.resolve(data);
+
+      Telemetry.getHistogramById("FX_SESSION_RESTORE_READ_FILE_MS").add(durationMs);
+      Telemetry.getHistogramById("FX_SESSION_RESTORE_FILE_SIZE_BYTES").add(byteLength);
     });
+
+    return deferred.promise;
   },
 
   write: function (aData) {
     if (this._isClosed) {
       return Promise.reject(new Error("SessionFile is closed"));
     }
     let refObj = {};
 
--- a/browser/components/sessionstore/src/SessionWorker.js
+++ b/browser/components/sessionstore/src/SessionWorker.js
@@ -69,38 +69,20 @@ let Agent = {
 
   // The path to sessionstore.js
   path: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"),
 
   // The path to sessionstore.bak
   backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"),
 
   /**
-   * Read the session from disk.
-   * In case sessionstore.js does not exist, attempt to read sessionstore.bak.
+   * NO-OP to start the worker.
    */
-  read: function () {
-    for (let path of [this.path, this.backupPath]) {
-      try {
-        let durationMs = Date.now();
-        let bytes = File.read(path);
-        durationMs = Date.now() - durationMs;
-        this.initialState = Decoder.decode(bytes);
-
-        return {
-          result: this.initialState,
-          telemetry: {FX_SESSION_RESTORE_READ_FILE_MS: durationMs,
-                      FX_SESSION_RESTORE_FILE_SIZE_BYTES: bytes.byteLength}
-        };
-      } catch (ex if isNoSuchFileEx(ex)) {
-        // Ignore exceptions about non-existent files.
-      }
-    }
-    // No sessionstore data files found. Return an empty string.
-    return {result: ""};
+  init: function () {
+    return {result: true};
   },
 
   /**
    * Write the session to disk.
    */
   write: function (stateString) {
     let exn;
     let telemetry = {};