Browse Source

Lazily listen for quit-application on the first sync-async call and bail out if the notification is sent while waiting for the callback.

Edward Lee 15 years ago
parent
commit
5d78514057
2 changed files with 47 additions and 2 deletions
  1. 23 2
      Sync.js
  2. 24 0
      test/unit/test_Sync_quit_app.js

+ 23 - 2
Sync.js

@@ -51,6 +51,27 @@ const CB_FAIL = {};
 // Share a secret only for functions in this file to prevent outside access
 const SECRET = {};
 
+/**
+ * Check if the app is ready (not quitting)
+ */
+function checkAppReady() {
+  // Watch for app-quit notification to stop any sync. calls
+  let os = Cc["@mozilla.org/observer-service;1"].
+    getService(Ci.nsIObserverService);
+  os.addObserver({
+    observe: function observe() {
+      // Now that the app is quitting, make checkAppReady throw
+      checkAppReady = function() {
+        throw Components.Exception("App. Quitting", Cr.NS_ERROR_ABORT);
+      };
+      os.removeObserver(this, "quit-application");
+    }
+  }, "quit-application", false);
+
+  // In the common case, checkAppReady just returns true
+  return (checkAppReady = function() true)();
+};
+
 /**
  * Create a callback that remembers state like whether it's been called
  */
@@ -115,9 +136,9 @@ function Sync(func, thisArg, callback) {
     // Call the async function bound to thisArg with the passed args
     func.apply(thisArg, args);
 
-    // Keep waiting until our callback is triggered
+    // Keep waiting until our callback is triggered unless the app is quitting
     let callbackData = instanceCallback._(SECRET);
-    while (callbackData.state == CB_READY)
+    while (checkAppReady() && callbackData.state == CB_READY)
       thread.processNextEvent(true);
 
     // Reset the state of the callback to prepare for another call

+ 24 - 0
test/unit/test_Sync_quit_app.js

@@ -0,0 +1,24 @@
+Cu.import("resource://jsmodules/Sync.js");
+
+function test_quit_app() {
+  // Signal the quit-application notification after half a second
+  setTimeout(function() {
+    Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService).
+      notifyObservers(null, "quit-application", null);
+  }, 500);
+
+  try {
+    Sync.sleep(100);
+  }
+  catch(ex) {
+    do_throw("Sleep shouldn't have thrown!");
+  }
+
+  try {
+    Sync.sleep(1000);
+    do_throw("Sleep should have failed because we're quitting!");
+  }
+  catch(ex if ex.result == Cr.NS_ERROR_ABORT) {
+    // We're expecting this exception, so let it through
+  }
+}