// BEGIN FLOCK GPL // // Copyright Flock Inc. 2005-2007 // http://flock.com // // This file may be used under the terms of of the // GNU General Public License Version 2 or later (the "GPL"), // http://www.gnu.org/licenses/gpl.html // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License // for the specific language governing rights and limitations under the // License. // // END FLOCK GPL const CC = Components.classes; const CI = Components.interfaces; const CR = Components.results; var EXPORTED_SYMBOLS = ["FlockScheduler"]; /** * FlockScheduler.schedule() - A cheap scheduler for javascript tasks. * * @param object[] aTimers (in) (optional) * If provided, any timers created will be stored in this array * so the caller can cancel them early. * @param float aSlice (in) * Maximum timeslice in seconds. 0.5 or less is a good value. * @param float aPercent (in) * Percent of wall-clock time that should be spent executing the task. * For example, 20 = one fifth of the time. * @param function aTask (in) * A generator taking a function that evaluates to true if it should * yield, like this: * * function task(should_yield) { * while (some_condition) { * do some work... * if (should_yield()) { * yield; * } * } * } * * @return nothing * * NOTE: should_yield() is fairly cheap but in a tight inner loop it might * make sense to call it every N cycles */ var FlockScheduler = { schedule: function schedule(aTimers, aSlice, aPercent, aTask) { // convert percent to ratio var ratio = aPercent / 100.0; // convert slice to milliseconds var slice = aSlice * 1000.0; // A helper to get the current time as milliseconds. function milliseconds() { return (new Date ()).getTime(); } var wall_start = milliseconds(); var process_time = 0; var process_start = 0; // A function the task can use to decide whether to yield its timeslice. function should_yield() { var now = milliseconds(); var so_far = now - process_start; var wall_time = now - wall_start; return (so_far >= slice) || ((wall_time * ratio) < (process_time + so_far)); } // A generator is (among other things) a function that calls yield(). // To resume execution at the point of suspension by yield(), call the // generator's .next() method. // See https://bugzilla.mozilla.org/show_bug.cgi?id=326466 var generator = aTask(should_yield); var timer = CC["@mozilla.org/timer;1"].createInstance(CI.nsITimer); var callback = { notify: function FlockScheduler_callback_notify() { var wall_time = milliseconds() - wall_start; if (process_time > (wall_time * ratio)) { // We've spent more time than we're supposed to; skip this cycle. return; } process_start = milliseconds(); try { // Resume execution where the function yield()ed. generator.next(); } catch (err if err instanceof StopIteration) { // The task is complete; stop timer and destroy it. timer.cancel(); if (aTimers) { for (var i = 0; i < aTimers.length; i++) { if (aTimers[i] === timer) { aTimers.splice(i, 1); } } } timer = null; } process_time += (milliseconds() - process_start); } }; timer.initWithCallback(callback, Math.round(slice/ratio), CI.nsITimer.TYPE_REPEATING_SLACK); if (aTimers) { aTimers.push(timer); } } };