gnome-shell/js/misc/batch.js
Ray Strode 2141138a6f gdm: move batch.js to misc/
batch.js has nothing to do with GDM, other than it's used by the login
dialog.

We eventually want to get rid of it entirely, but in the mean time move
it to misc/ for clarity.
2013-06-18 08:01:27 -04:00

204 lines
5.2 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const Lang = imports.lang;
const Signals = imports.signals;
const Task = new Lang.Class({
Name: 'Task',
_init: function(scope, handler) {
if (scope)
this.scope = scope;
else
this.scope = this;
this.handler = handler;
},
run: function() {
if (this.handler)
return this.handler.call(this.scope);
return null;
},
});
Signals.addSignalMethods(Task.prototype);
const Hold = new Lang.Class({
Name: 'Hold',
Extends: Task,
_init: function() {
this.parent(this, function () {
return this;
});
this._acquisitions = 1;
},
acquire: function() {
if (this._acquisitions <= 0)
throw new Error("Cannot acquire hold after it's been released");
this._acquisitions++;
},
acquireUntilAfter: function(hold) {
if (!hold.isAcquired())
return;
this.acquire();
let signalId = hold.connect('release', Lang.bind(this, function() {
hold.disconnect(signalId);
this.release();
}));
},
release: function() {
this._acquisitions--;
if (this._acquisitions == 0)
this.emit('release');
},
isAcquired: function() {
return this._acquisitions > 0;
}
});
Signals.addSignalMethods(Hold.prototype);
const Batch = new Lang.Class({
Name: 'Batch',
Extends: Task,
_init: function(scope, tasks) {
this.parent();
this.tasks = [];
for (let i = 0; i < tasks.length; i++) {
let task;
if (tasks[i] instanceof Task) {
task = tasks[i];
} else if (typeof tasks[i] == 'function') {
task = new Task(scope, tasks[i]);
} else {
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
}
this.tasks.push(task);
}
},
process: function() {
throw new Error('Not implemented');
},
runTask: function() {
if (!(this._currentTaskIndex in this.tasks)) {
return null;
}
return this.tasks[this._currentTaskIndex].run();
},
_finish: function() {
this.hold.release();
},
nextTask: function() {
this._currentTaskIndex++;
// if the entire batch of tasks is finished, release
// the hold and notify anyone waiting on the batch
if (this._currentTaskIndex >= this.tasks.length) {
this._finish();
return;
}
this.process();
},
_start: function() {
// acquire a hold to get released when the entire
// batch of tasks is finished
this.hold = new Hold();
this._currentTaskIndex = 0;
this.process();
},
run: function() {
this._start();
// hold may be destroyed at this point
// if we're already done running
return this.hold;
},
cancel: function() {
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
}
});
Signals.addSignalMethods(Batch.prototype);
const ConcurrentBatch = new Lang.Class({
Name: 'ConcurrentBatch',
Extends: Batch,
process: function() {
let hold = this.runTask();
if (hold) {
this.hold.acquireUntilAfter(hold);
}
// Regardless of the state of the just run task,
// fire off the next one, so all the tasks can run
// concurrently.
this.nextTask();
}
});
Signals.addSignalMethods(ConcurrentBatch.prototype);
const ConsecutiveBatch = new Lang.Class({
Name: 'ConsecutiveBatch',
Extends: Batch,
process: function() {
let hold = this.runTask();
if (hold && hold.isAcquired()) {
// This task is inhibiting the batch. Wait on it
// before processing the next one.
let signalId = hold.connect('release',
Lang.bind(this, function() {
hold.disconnect(signalId);
this.nextTask();
}));
return;
} else {
// This task finished, process the next one
this.nextTask();
}
}
});
Signals.addSignalMethods(ConsecutiveBatch.prototype);