Bug 1509267 - Cleanup Remote Settings worker when inactive r=glasserc,dthayer
authorMathieu Leplatre <mathieu@mozilla.com>
Thu, 16 May 2019 09:10:34 +0000
changeset 535956 5765e6384ff13d09dce8580df9abb4ed63178a31
parent 535955 9faf24ffeaaf5f1e38fca1c6630716aeca7982bf
child 535957 d7367a8f16debee5c2efbf337df61cc2a25421ef
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglasserc, dthayer
bugs1509267
milestone68.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 1509267 - Cleanup Remote Settings worker when inactive r=glasserc,dthayer Differential Revision: https://phabricator.services.mozilla.com/D31080
services/settings/RemoteSettingsWorker.jsm
--- a/services/settings/RemoteSettingsWorker.jsm
+++ b/services/settings/RemoteSettingsWorker.jsm
@@ -2,47 +2,72 @@
  * 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";
 
 /**
  * Interface to a dedicated thread handling for Remote Settings heavy operations.
  */
-
-// ChromeUtils.import("resource://gre/modules/PromiseWorker.jsm", this);
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const { setTimeout, clearTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
 var EXPORTED_SYMBOLS = ["RemoteSettingsWorker"];
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "gMaxIdleMilliseconds",
+  "services.settings.worker_idle_max_milliseconds",
+  30 * 1000 // Default of 30 seconds.
+);
+
 class Worker {
   constructor(source) {
-    this.worker = new ChromeWorker(source);
-    this.worker.onmessage = this._onWorkerMessage.bind(this);
+    this.source = source;
+    this.worker = null;
 
     this.callbacks = new Map();
     this.lastCallbackId = 0;
+    this.idleTimeoutId = null;
   }
 
   async _execute(method, args = []) {
-    return new Promise(async (resolve, reject) => {
+    // (Re)instantiate the worker if it was terminated.
+    if (!this.worker) {
+      this.worker = new ChromeWorker(this.source);
+      this.worker.onmessage = this._onWorkerMessage.bind(this);
+    }
+    // New activity: reset the idle timer.
+    if (this.idleTimeoutId) {
+      clearTimeout(this.idleTimeoutId);
+    }
+    return new Promise((resolve, reject) => {
       const callbackId = ++this.lastCallbackId;
       this.callbacks.set(callbackId, [resolve, reject]);
       this.worker.postMessage({ callbackId, method, args });
     });
   }
 
   _onWorkerMessage(event) {
     const { callbackId, result, error } = event.data;
     const [resolve, reject] = this.callbacks.get(callbackId);
     if (error) {
       reject(new Error(error));
     } else {
       resolve(result);
     }
     this.callbacks.delete(callbackId);
+
+    // Terminate the worker when it's unused for some time.
+    // But don't terminate it if an operation is pending.
+    if (this.callbacks.length == 0) {
+      this.idleTimeoutId = setTimeout(() => {
+        this.worker.terminate();
+        this.worker = null;
+        this.idleTimeoutId = null;
+      }, gMaxIdleMilliseconds);
+    }
   }
 
   async canonicalStringify(localRecords, remoteRecords, timestamp) {
     return this._execute("canonicalStringify", [localRecords, remoteRecords, timestamp]);
   }
 
   async importJSONDump(bucket, collection) {
     return this._execute("importJSONDump", [bucket, collection]);