File "ReadWriteLock.js"

Full Path: /srv/www/www.cadoro.it/app/lib/locks/lib/ReadWriteLock.js
File size: 2.74 KB
MIME-type: text/plain
Charset: utf-8

function ReadWriteLock() {
	this.isLocked = false;
	this._readLocks = 0;
	this._waitingToRead = [];
	this._waitingToWrite = [];
}

module.exports = ReadWriteLock;


ReadWriteLock.prototype.readLock = function (cb) {
	if (this.isLocked === 'W') {
		this._waitingToRead.push(cb);
	} else {
		this._readLocks += 1;
		this.isLocked = 'R';
		cb.call(this);
	}
};


ReadWriteLock.prototype.writeLock = function (cb) {
	if (this.isLocked) {
		this._waitingToWrite.push(cb);
	} else {
		this.isLocked = 'W';
		cb.call(this);
	}
};


ReadWriteLock.prototype.timedReadLock = function (ttl, cb) {
	if (this.tryReadLock()) {
		return cb.call(this);
	}

	var timer, that = this;

	function waiter() {
		clearTimeout(timer);

		if (cb) {
			var callback = cb;
			cb = null;
			callback.apply(that, arguments);
		}
	}

	this._waitingToRead.push(waiter);

	timer = setTimeout(function () {
		var index = that._waitingToRead.indexOf(waiter);
		if (index !== -1) {
			that._waitingToRead.splice(index, 1);
			waiter(new Error('ReadLock timed out'));
		}
	}, ttl);
};


ReadWriteLock.prototype.timedWriteLock = function (ttl, cb) {
	if (this.tryWriteLock()) {
		return cb.call(this);
	}

	var timer, that = this;

	function waiter() {
		clearTimeout(timer);

		if (cb) {
			var callback = cb;
			cb = null;
			callback.apply(that, arguments);
		}
	}

	this._waitingToWrite.push(waiter);

	timer = setTimeout(function () {
		var index = that._waitingToWrite.indexOf(waiter);
		if (index !== -1) {
			that._waitingToWrite.splice(index, 1);
			waiter(new Error('WriteLock timed out'));
		}
	}, ttl);
};


ReadWriteLock.prototype.tryReadLock = function () {
	if (this.isLocked === 'W') {
		return false;
	}

	this.isLocked = 'R';
	this._readLocks += 1;
	return true;
};


ReadWriteLock.prototype.tryWriteLock = function () {
	if (this.isLocked) {
		return false;
	}

	this.isLocked = 'W';
	return true;
};


ReadWriteLock.prototype.unlock = function () {
	var waiter;

	if (this.isLocked === 'R') {
		this._readLocks -= 1;

		if (this._readLocks === 0) {
			// allow one write lock through

			waiter = this._waitingToWrite.shift();
			if (waiter) {
				this.isLocked = 'W';
				waiter.call(this);
			} else {
				this.isLocked = false;
			}
		}
	} else if (this.isLocked === 'W') {
		// allow all read locks or one write lock through

		var rlen = this._waitingToRead.length;

		if (rlen === 0) {
			waiter = this._waitingToWrite.shift();
			if (waiter) {
				this.isLocked = 'W';
				waiter.call(this);
			} else {
				this.isLocked = false;
			}
		} else {
			this.isLocked = 'R';
			this._readLocks = rlen;

			var waiters = this._waitingToRead.slice();
			this._waitingToRead = [];

			for (var i = 0; i < rlen; i++) {
				waiters[i].call(this);
			}
		}
	} else {
		throw new Error('ReadWriteLock is not locked');
	}
};