Browse Source

Allow sync. functions to be called more than once and have each sync. invocation instance separate from each other.

Edward Lee 15 years ago
parent
commit
c7d60c5259
2 changed files with 51 additions and 18 deletions
  1. 38 18
      Sync.js
  2. 13 0
      test/unit/test_Sync.js

+ 38 - 18
Sync.js

@@ -43,6 +43,10 @@ const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
+// Define some constants to specify various sync. callback states
+const CB_READY = {};
+const CB_COMPLETE = {};
+
 /**
  * Make a synchronous version of the function object that will be called with
  * the provided thisArg.
@@ -56,46 +60,62 @@ const Cu = Components.utils;
  * @usage let sync = Sync(async); let ret = sync(arg1, arg2);
  */
 function Sync(func, thisArg) {
-  // Hold the value passed in from the callback to return
-  let retval;
-
-  // Track if the onComplete has been called
-  let gotComplete = false;
-  let onComplete = function(data) {
-    retval = data;
-    gotComplete = true;
+  // Create a callback that remembers its state
+  let makeCallback = function Sync_makeCallback() {
+    // The main callback remembers the value it's passed and that it got data
+    let onComplete = function Sync_onComplete(data) {
+      onComplete.value = data;
+      onComplete.state = CB_COMPLETE;
+    };
+
+    // Initialize the callback to wait to be called
+    onComplete.state = CB_READY;
+
+    return onComplete;
   };
 
-  // Determine if the custom callback should be passed in as the first arg
-  let insertCallback = true;
+  // If the sync. function callback is extracted, remember what it is
+  let extractedCallback;
 
-  let syncFunc = function(/* arg1, arg2, ... */) {
+  let syncFunc = function Sync_syncFunc(/* arg1, arg2, ... */) {
     // Grab the current thread so we can make it give up priority
     let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
 
     // Save the original arguments into an array
     let args = Array.slice(arguments);
 
-    // Inject our custom callback as the first argument for the async function
-    if (insertCallback)
-      args.unshift(onComplete);
+    // Track the callback used for this sync. invocation instance
+    let instanceCallback = extractedCallback;
+
+    // We need to add a callback if it wasn't extracted out beforehand
+    if (instanceCallback == null) {
+      // Create a new callback for this invocation instance and pass it in
+      instanceCallback = makeCallback();
+      args.unshift(instanceCallback);
+    }
 
     // Call the async function bound to thisArg with the passed args
     func.apply(thisArg, args);
 
     // Keep waiting until our callback is triggered
-    while (!gotComplete)
+    while (instanceCallback.state == CB_READY)
       thread.processNextEvent(true);
 
+    // Reset the state of the callback to prepare for another call
+    instanceCallback.state = CB_READY;
+
     // Return the value passed to the callback
-    return retval;
+    return instanceCallback.value;
   };
 
   // Grabbing the onComplete converts the sync. function to not assume the first
   // argument is our custom callback
   syncFunc.__defineGetter__("onComplete", function() {
-    insertCallback = false;
-    return onComplete;
+    // Only allow one callback to be made, so delete the getter immediately
+    delete syncFunc.onComplete;
+
+    // Remember the one callback made as a property and locally
+    return syncFunc.onComplete = extractedCallback = makeCallback();
   });
 
   return syncFunc;

+ 13 - 0
test/unit/test_Sync.js

@@ -31,6 +31,19 @@ function test_Sync_sleep() {
   }, 100);
 }
 
+// Make sure Sync.sleep can be called more than once
+function test_Sync_multiple() {
+  checkTime(function() {
+    Sync.sleep(100);
+    checkTime(function() {
+      Sync.sleep(100);
+      checkTime(function() {
+        Sync.sleep(100);
+      }, 100);
+    }, 200);
+  }, 300);
+}
+
 // Check that we can create a sync. function
 function test_Sync() {
   checkTime(function() {