Bug 1273920 P3b Trigger cycle collector instead of using a timeout. r=bkelly
authorAndrew Sutherland <asutherland@asutherland.org>
Mon, 23 May 2016 22:40:50 -0400
changeset 337823 36ed89b1851c88b757a2a2a9a9a911e2598f4f36
parent 337822 013b6cecd6ead3f2ef504202cbd240ae95d60541
child 337824 3313a2e169d161104009e905a662e13be8a757f1
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1273920
milestone49.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 1273920 P3b Trigger cycle collector instead of using a timeout. r=bkelly
dom/workers/test/serviceworkers/blocking_install_event_worker.js
dom/workers/test/serviceworkers/test_install_event_gc.html
--- a/dom/workers/test/serviceworkers/blocking_install_event_worker.js
+++ b/dom/workers/test/serviceworkers/blocking_install_event_worker.js
@@ -10,8 +10,14 @@ function postMessageToTest(msg) {
     });
 }
 
 addEventListener('install', evt => {
   // This must be a simple promise to trigger the CC failure.
   evt.waitUntil(new Promise(function() { }));
   postMessageToTest({ type: 'INSTALL_EVENT' });
 });
+
+addEventListener('message', evt => {
+  if (evt.data.type === 'ping') {
+    postMessageToTest({ type: 'pong' });
+  }
+});
--- a/dom/workers/test/serviceworkers/test_install_event_gc.html
+++ b/dom/workers/test/serviceworkers/test_install_event_gc.html
@@ -33,23 +33,59 @@ function waitForInstallEvent() {
       if (evt.data.type === 'INSTALL_EVENT') {
         resolve();
       }
     });
   });
 }
 
 function gcWorker() {
-  return new Promise(function(resolve) {
-    // XXX: We need to trigger a CC/GC on the worker thread, but special
-    //      powers does not support that.  A setTimeout() longer than our
-    //      CC period is the only solution I have found.
-    SimpleTest.requestFlakyTimeout('No way to force an immediate CC/GC of a ' +
-                                   'worker JS runtime.');
-    setTimeout(resolve, 10000);
+  return new Promise(function(resolve, reject) {
+    // We are able to trigger asynchronous garbage collection and cycle
+    // collection by emitting "child-cc-request" and "child-gc-request"
+    // observer notifications.  The worker RuntimeService will translate
+    // these notifications into the appropriate operation on all known
+    // worker threads.
+    //
+    // In the failure case where GC/CC causes us to abort the installation,
+    // we will know something happened from the statechange event.
+    const statechangeHandler = evt => {
+      // Reject rather than resolving to avoid the possibility of us seeing
+      // an unrelated racing statechange somehow.  Since in the success case we
+      // will still see a state change on termination, we do explicitly need to
+      // be removed on the success path.
+      ok(registration.installing, 'service worker is still installing?');
+      reject();
+    };
+    registration.installing.addEventListener('statechange', statechangeHandler);
+    // In the success case since the service worker installation is effectively
+    // hung, we instead depend on sending a 'ping' message to the service worker
+    // and hearing it 'pong' back.  Since we issue our postMessage after we
+    // trigger the GC/CC, our 'ping' will only be processed after the GC/CC and
+    // therefore the pong will also strictly occur after the cycle collection.
+    navigator.serviceWorker.addEventListener('message', evt => {
+      if (evt.data.type === 'pong') {
+        registration.installing.removeEventListener(
+          'statechange', statechangeHandler);
+        resolve();
+      }
+    });
+    // At the current time, the service worker will exist in our same process
+    // and notifyObservers is synchronous.  However, in the future, service
+    // workers may end up in a separate process and in that case it will be
+    // appropriate to use notifyObserversInParentProcess or something like it.
+    // (notifyObserversInParentProcess is a synchronous IPC call to the parent
+    // process's main thread.  IPDL PContent::CycleCollect is an async message.
+    // Ordering will be maintained if the postMessage goes via PContent as well,
+    // but that seems unlikely.)
+    SpecialPowers.notifyObservers(null, 'child-gc-request', null);
+    SpecialPowers.notifyObservers(null, 'child-cc-request', null);
+    SpecialPowers.notifyObservers(null, 'child-gc-request', null);
+    // (Only send the ping after we set the gc/cc/gc in motion.)
+    registration.installing.postMessage({ type: 'ping' });
   });
 }
 
 function terminateWorker() {
   return SpecialPowers.pushPrefEnv({
     set: [
       ["dom.serviceWorkers.idle_timeout", 0],
       ["dom.serviceWorkers.idle_extended_timeout", 0]
@@ -78,9 +114,8 @@ SpecialPowers.pushPrefEnv({"set": [
   ["dom.serviceWorkers.enabled", true],
   ["dom.serviceWorkers.testing.enabled", true],
   ["dom.caches.enabled", true],
 ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
-