Bug 959130 - Stop using the SessionWorker for read at startup to avoid slowdown. r=ttaubert
authorSteven MacLeod <smacleod@mozilla.com>
Mon, 03 Feb 2014 15:14:06 +0100
changeset 182629 13d1ff429b44e99b17a709810633d0bc638d2665
parent 182628 9b281bb1bbeac265da027d29757c89f899c97590
child 182630 75303a3ddc0c1f6ec245252c85d6075b8f73b986
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersttaubert
bugs959130
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 959130 - Stop using the SessionWorker for read at startup to avoid slowdown. r=ttaubert
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, "console",
   "resource://gre/modules/devtools/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
@@ -120,20 +123,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;
   },
 
   gatherTelemetry: function(aStateString) {
     return Task.spawn(function() {
       let msg = yield SessionWorker.post("gatherTelemetry", [aStateString]);
       this._recordTelemetry(msg.telemetry);
       throw new Task.Result(msg.telemetry);
     }.bind(this));
--- a/browser/components/sessionstore/src/SessionWorker.js
+++ b/browser/components/sessionstore/src/SessionWorker.js
@@ -62,37 +62,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;
-
-        return {
-          result: Decoder.decode(bytes),
-          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 = {};