Sync.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is Weave.
  15. *
  16. * The Initial Developer of the Original Code is Mozilla.
  17. * Portions created by the Initial Developer are Copyright (C) 2009
  18. * the Initial Developer. All Rights Reserved.
  19. *
  20. * Contributor(s):
  21. * Edward Lee <edilee@mozilla.com>
  22. * Dan Mills <thunder@mozilla.com>
  23. * Myk Melez <myk@mozilla.org>
  24. *
  25. * Alternatively, the contents of this file may be used under the terms of
  26. * either the GNU General Public License Version 2 or later (the "GPL"), or
  27. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28. * in which case the provisions of the GPL or the LGPL are applicable instead
  29. * of those above. If you wish to allow use of your version of this file only
  30. * under the terms of either the GPL or the LGPL, and not to allow others to
  31. * use your version of this file under the terms of the MPL, indicate your
  32. * decision by deleting the provisions above and replace them with the notice
  33. * and other provisions required by the GPL or the LGPL. If you do not delete
  34. * the provisions above, a recipient may use your version of this file under
  35. * the terms of any one of the MPL, the GPL or the LGPL.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. let EXPORTED_SYMBOLS = ["Sync"];
  39. const Cc = Components.classes;
  40. const Ci = Components.interfaces;
  41. const Cr = Components.results;
  42. const Cu = Components.utils;
  43. // Define some constants to specify various sync. callback states
  44. const CB_READY = {};
  45. const CB_COMPLETE = {};
  46. const CB_FAIL = {};
  47. // Share a secret only for functions in this file to prevent outside access
  48. const SECRET = {};
  49. /**
  50. * Check if the app is ready (not quitting)
  51. */
  52. function checkAppReady() {
  53. // Watch for app-quit notification to stop any sync. calls
  54. let os = Cc["@mozilla.org/observer-service;1"].
  55. getService(Ci.nsIObserverService);
  56. os.addObserver({
  57. observe: function observe() {
  58. // Now that the app is quitting, make checkAppReady throw
  59. checkAppReady = function() {
  60. throw Components.Exception("App. Quitting", Cr.NS_ERROR_ABORT);
  61. };
  62. os.removeObserver(this, "quit-application");
  63. }
  64. }, "quit-application", false);
  65. // In the common case, checkAppReady just returns true
  66. return (checkAppReady = function() true)();
  67. };
  68. /**
  69. * Create a callback that remembers state like whether it's been called
  70. */
  71. function makeCallback() {
  72. // Initialize private callback data to prepare to be called
  73. let _ = {
  74. state: CB_READY,
  75. value: null
  76. };
  77. // The main callback remembers the value it's passed and that it got data
  78. let onComplete = function makeCallback_onComplete(data) {
  79. _.state = CB_COMPLETE;
  80. _.value = data;
  81. };
  82. // Only allow access to the private data if the secret matches
  83. onComplete._ = function onComplete__(secret) secret == SECRET ? _ : {};
  84. // Allow an alternate callback to trigger an exception to be thrown
  85. onComplete.throw = function onComplete_throw(data) {
  86. _.state = CB_FAIL;
  87. _.value = data;
  88. // Cause the caller to get an exception and stop execution
  89. throw data;
  90. };
  91. return onComplete;
  92. }
  93. /**
  94. * Make a synchronous version of the function object that will be called with
  95. * the provided thisArg.
  96. *
  97. * @param func {Function}
  98. * The asynchronous function to make a synchronous function
  99. * @param thisArg {Object} [optional]
  100. * The object that the function accesses with "this"
  101. * @param callback {Function} [optional] [internal]
  102. * The callback that will trigger the end of the async. call
  103. * @usage let ret = Sync(asyncFunc, obj)(arg1, arg2);
  104. * @usage let ret = Sync(ignoreThisFunc)(arg1, arg2);
  105. * @usage let sync = Sync(async); let ret = sync(arg1, arg2);
  106. */
  107. function Sync(func, thisArg, callback) {
  108. return function syncFunc(/* arg1, arg2, ... */) {
  109. // Grab the current thread so we can make it give up priority
  110. let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
  111. // Save the original arguments into an array
  112. let args = Array.slice(arguments);
  113. let instanceCallback = callback;
  114. // We need to create a callback and insert it if we weren't given one
  115. if (instanceCallback == null) {
  116. // Create a new callback for this invocation instance and pass it in
  117. instanceCallback = makeCallback();
  118. args.unshift(instanceCallback);
  119. }
  120. // Call the async function bound to thisArg with the passed args
  121. func.apply(thisArg, args);
  122. // Keep waiting until our callback is triggered unless the app is quitting
  123. let callbackData = instanceCallback._(SECRET);
  124. while (checkAppReady() && callbackData.state == CB_READY)
  125. thread.processNextEvent(true);
  126. // Reset the state of the callback to prepare for another call
  127. let state = callbackData.state;
  128. callbackData.state = CB_READY;
  129. // Throw the value the callback decided to fail with
  130. if (state == CB_FAIL)
  131. throw callbackData.value;
  132. // Return the value passed to the callback
  133. return callbackData.value;
  134. };
  135. }
  136. /**
  137. * Make a synchronous version of an async. function and the callback to trigger
  138. * the end of the async. call.
  139. *
  140. * @param func {Function}
  141. * The asynchronous function to make a synchronous function
  142. * @param thisArg {Object} [optional]
  143. * The object that the function accesses with "this"
  144. * @usage let [sync, cb] = Sync.withCb(async); let ret = sync(arg1, arg2, cb);
  145. */
  146. Sync.withCb = function Sync_withCb(func, thisArg) {
  147. let cb = makeCallback();
  148. return [Sync(func, thisArg, cb), cb];
  149. };
  150. /**
  151. * Set a timer, simulating the API for the window.setTimeout call.
  152. * This only simulates the API for the version of the call that accepts
  153. * a function as its first argument and no additional parameters,
  154. * and it doesn't return the timeout ID.
  155. *
  156. * @param func {Function}
  157. * the function to call after the delay
  158. * @param delay {Number}
  159. * the number of milliseconds to wait
  160. */
  161. function setTimeout(func, delay) {
  162. let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  163. let callback = {
  164. notify: function notify() {
  165. // This line actually just keeps a reference to timer (prevent GC)
  166. timer = null;
  167. // Call the function so that "this" is global
  168. func();
  169. }
  170. }
  171. timer.initWithCallback(callback, delay, Ci.nsITimer.TYPE_ONE_SHOT);
  172. }
  173. function sleep(callback, milliseconds) {
  174. setTimeout(callback, milliseconds);
  175. }
  176. /**
  177. * Sleep the specified number of milliseconds, pausing execution of the caller
  178. * without halting the current thread.
  179. * For example, the following code pauses 1000ms between dumps:
  180. *
  181. * dump("Wait for it...\n");
  182. * Sync.sleep(1000);
  183. * dump("Wait for it...\n");
  184. * Sync.sleep(1000);
  185. * dump("What are you waiting for?!\n");
  186. *
  187. * @param milliseconds {Number}
  188. * The number of milliseconds to sleep
  189. */
  190. Sync.sleep = Sync(sleep);