Index: lib/counter_task.js |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/lib/counter_task.js |
@@ -0,0 +1,117 @@ |
+let {Logger} = require( "logger" ); |
+ |
+/** |
+ * This task just counts. |
+ * |
+ * @param count |
+ * The number of iterations. |
+ * @param count_notifier |
+ * The count notifier is called once at each iteration with this current loop counter. |
+ * @param completion_notifier |
+ * The completion notifier is called when the task is finished. Because a long running task does not run |
+ * synchronously, we need some kind of notification system. |
+ * @param {Boolean} [use_slow_version=true] |
+ */ |
+var Counting_Task = exports.Counting_Task = function( count, count_notifier, completion_notifier, variant ) |
+{ |
+ this.count = count; |
+ this.count_notifier = count_notifier; |
+ this.completion_notifier = completion_notifier; |
+ |
+ this.logger = new Logger( "Counting_Task" ); |
+ |
+ switch ( variant.type ) |
+ { |
+ case "segmented fast": |
+ this.segmented = true; |
+ this._dispatch_self = function() |
+ { |
+ this.thread_manager.currentThread.dispatch( { run: this.land }, Ci.nsIEventTarget.DISPATCH_NORMAL ); |
+ }; |
+ /** |
+ * Thread manager is used to make counting an asynchronous operation. |
+ * @type {nsIThreadManager} |
+ */ |
+ this.thread_manager = Cc["@mozilla.org/thread-manager;1"].createInstance( Ci.nsIThreadManager ); |
+ break; |
+ case "segmented slow": |
+ this.segmented = true; |
+ this._dispatch_self = function() |
+ { |
+ this.timer.initWithCallback( this.land.bind( this ), 100, Ci.nsITimer.TYPE_ONE_SHOT ); |
+ }; |
+ /** |
+ * This task uses a timer to slow down the count in order to provide a simulation of a slow task to exercise |
+ * pause-resume functionality. |
+ * @type {*} |
+ */ |
+ this.timer = Cc["@mozilla.org/timer;1"].createInstance( Ci.nsITimer ); |
+ break; |
+ case "continuous": |
+ this.segmented = false; |
+ break; |
+ default: |
+ throw "Unknown Counting_task variant"; |
+ } |
+ this.logger.make_log()( "using " + variant.type + " variant" ); |
+}; |
+ |
+/** |
+ * The generator for the task. |
+ * |
+ * @param {Function} pause |
+ * @param {Function} resume |
+ */ |
+Counting_Task.prototype.generator = function( pause, resume ) |
+{ |
+ var log = this.logger.make_log( "task" ); |
+ log( "begin" ); |
+ |
+ /** |
+ * Flag indicating if there's a pending asynchronous operation. |
+ * @type {Boolean} |
+ */ |
+ var pending = false; |
+ |
+ /** |
+ * Internal landing function for segmented variants. Defined here to gain access to the scope variables in the chain |
+ * of the present invocation of the function. |
+ */ |
+ this.land = function() |
+ { |
+ pending = false; |
+ resume(); |
+ }; |
+ |
+ var j; |
+ for ( j = 0 ; j < this.count ; ++j ) |
+ { |
+ if ( this.segmented && !pending ) |
+ { |
+ this._dispatch_self(); |
+ pause(); |
+ pending = true; |
+ } |
+ /* |
+ * The rvalue of a yield statement is the argument to 'send()' called on the generator. The task runner |
+ * calls 'send( true )' to indicated cancellation. 'next()' is a synonym for 'send( undefined )'. Thus, |
+ * the possible values for 'cancelled' are 'true' and 'undefined'. |
+ */ |
+ var cancelled = yield false ; |
+ if ( cancelled ) |
+ { |
+ log( "cancelled" ); |
+ break; |
+ } |
+ if ( this.count_notifier ) |
+ this.count_notifier( j ); |
+ } |
+ if ( j == this.count ) |
+ { |
+ // Assert the loop terminated in the 'for' statement, not with a cancellation. |
+ log( "finished" ) |
+ } |
+ if ( this.completion_notifier ) |
+ this.completion_notifier(); |
+ log( "end" ); |
+}; |