Commit 591874ff authored by yoshiki@chromium.org's avatar yoshiki@chromium.org

Files.app: Introduce AsyncUtil.ConcurrentQueue

This patch introduces AsyncUtil.ConcurrentQueue class, which is almost same as AsyncUtil.Queue but it can run multiple jobs concurrently.

BUG=367123
TEST=Files.app works well

Review URL: https://codereview.chromium.org/293053006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272934 0039d316-1c4b-4281-b951-d872f2087c98
parent 528a52b0
...@@ -43,57 +43,126 @@ AsyncUtil.forEach = function( ...@@ -43,57 +43,126 @@ AsyncUtil.forEach = function(
/** /**
* Creates a class for executing several asynchronous closures in a fifo queue. * Creates a class for executing several asynchronous closures in a fifo queue.
* Added tasks will be executed sequentially in order they were added. * Added tasks will be started in order they were added. Tasks are run
* concurrently. At most, |limit| jobs will be run at the same time.
* *
* @param {number} limit The number of jobs to run at the same time.
* @constructor * @constructor
*/ */
AsyncUtil.Queue = function() { AsyncUtil.ConcurrentQueue = function(limit) {
this.running_ = false; console.assert(limit > 0, '|limit| must be larger than 0');
this.closures_ = [];
this.limit_ = limit;
this.addedTasks_ = [];
this.pendingTasks_ = [];
this.isCancelled_ = false;
Object.seal(this);
}; };
/** /**
* @return {boolean} True when a task is running, otherwise false. * @return {boolean} True when a task is running, otherwise false.
*/ */
AsyncUtil.Queue.prototype.isRunning = function() { AsyncUtil.ConcurrentQueue.prototype.isRunning = function() {
return this.running_; return this.pendingTasks_.length !== 0;
};
/**
* @return {number} Number of waiting tasks.
*/
AsyncUtil.ConcurrentQueue.prototype.getWaitingTasksCount = function() {
return this.addedTasks_.length;
};
/**
* @return {boolean} Number of running tasks.
*/
AsyncUtil.ConcurrentQueue.prototype.getRunningTasksCount = function() {
return this.pendingTasks_.length;
}; };
/** /**
* Enqueues a closure to be executed. * Enqueues a closure to be executed.
* @param {function(function())} closure Closure with a completion callback to * @param {function(function())} closure Closure with a completion
* be executed. * callback to be executed.
*/ */
AsyncUtil.Queue.prototype.run = function(closure) { AsyncUtil.ConcurrentQueue.prototype.run = function(closure) {
this.closures_.push(closure); if (this.isCancelled_) {
if (!this.running_) console.error('Queue is calcelled. Cannot add a new task.');
this.continue_(); return;
}
this.addedTasks_.push(closure);
this.continue_();
}; };
/** /**
* Serves the next closure from the queue. * Cancels the queue. It removes all the not-run (yet) tasks. Note that this
* does NOT stop tasks currently running.
*/
AsyncUtil.ConcurrentQueue.prototype.cancel = function() {
this.isCancelled_ = true;
this.addedTasks_ = [];
};
/**
* @return {boolean} True when the queue have been requested to cancel or is
* already cancelled. Otherwise false.
*/
AsyncUtil.ConcurrentQueue.prototype.isCancelled = function() {
return this.isCancelled_;
};
/**
* Runs the next tasks if available.
* @private * @private
*/ */
AsyncUtil.Queue.prototype.continue_ = function() { AsyncUtil.ConcurrentQueue.prototype.continue_ = function() {
if (!this.closures_.length) { if (this.addedTasks_.length === 0)
this.running_ = false; return;
console.assert(
this.pendingTasks_.length <= this.limit_,
'Too many jobs are running (' + this.pendingTasks_.length + ')');
if (this.pendingTasks_.length >= this.limit_)
return; return;
}
// Run the next closure. // Run the next closure.
this.running_ = true; var closure = this.addedTasks_.shift();
var closure = this.closures_.shift(); this.pendingTasks_.push(closure);
closure(this.continue_.bind(this)); closure(this.onTaskFinished_.bind(this, closure));
this.continue_();
};
/**
* Called when a task is finished. Removes the tasks from pending task list.
* @param {function()} closure Finished task, which has been bound in
* |continue_|.
* @private
*/
AsyncUtil.ConcurrentQueue.prototype.onTaskFinished_ = function(closure) {
var index = this.pendingTasks_.indexOf(closure);
console.assert(index >= 0, 'Invalid task is finished');
this.pendingTasks_.splice(index, 1);
this.continue_();
}; };
/** /**
* Cancels all pending tasks. Note that this does NOT cancel the task running * Creates a class for executing several asynchronous closures in a fifo queue.
* currently. * Added tasks will be executed sequentially in order they were added.
*
* @constructor
* @extends {AsyncUtil.ConcurrentQueue}
*/ */
AsyncUtil.Queue.prototype.cancel = function() { AsyncUtil.Queue = function() {
this.closures_ = []; AsyncUtil.ConcurrentQueue.call(this, 1);
}; };
AsyncUtil.Queue.prototype.__proto__ = AsyncUtil.ConcurrentQueue.prototype;
/** /**
* Creates a class for executing several asynchronous closures in a group in * Creates a class for executing several asynchronous closures in a group in
* a dependency order. * a dependency order.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment