NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// ==UserLibrary==
// @name libLocks
// @namespace https://openuserjs.org/users/Anakunda
// @version 1.00
// @author Wizcorp Open Source
// @license MIT
// @copyright 2021, Wizcorp Open Source (https://github.com/Wizcorp)
// @description https://github.com/Wizcorp/locks
// @exclude *
// ==/UserScript==
// ==/UserLibrary==
function CondVariable(initialValue) {
this._value = initialValue;
this._waiting = [];
}
function condToFunc(cond) {
if (typeof cond === 'function') {
return cond;
}
if (typeof cond === 'number' || typeof cond === 'boolean' || typeof cond === 'string') {
return function (value) {
return value === cond;
};
}
if (cond && typeof cond === 'object' && cond instanceof RegExp) {
return function (value) {
return cond.test(value);
};
}
throw new TypeError('Unknown condition type: ' + (typeof cond));
}
CondVariable.prototype.get = function () {
return this._value;
};
CondVariable.prototype.wait = function (cond, cb) {
var test = condToFunc(cond);
if (test(this._value)) {
return cb.call(this);
}
this._waiting.push({ test: test, cb: cb });
};
CondVariable.prototype.set = function (value) {
this._value = value;
for (var i = 0; i < this._waiting.length; i++) {
var waiter = this._waiting[i];
if (waiter.test(value)) {
this._waiting.splice(i, 1);
i -= 1;
waiter.cb.call(this);
}
}
};
function Mutex() {
this._isLocked = false;
this._waiting = [];
}
Mutex.prototype.lock = function (cb) {
if (this._isLocked) {
this._waiting.push(cb);
} else {
this._isLocked = true;
cb.call(this);
}
};
Mutex.prototype.timedLock = function (ttl, cb) {
if (!this._isLocked) {
this._isLocked = true;
return cb.call(this);
}
var timer, that = this;
this._waiting.push(function () {
clearTimeout(timer);
if (!cb) {
that.unlock();
return;
}
cb.call(this);
cb = null;
});
timer = setTimeout(function () {
if (cb) {
cb.call(this, new Error('Lock timed out'));
cb = null;
}
}, ttl);
};
Object.defineProperty(Mutex.prototype, 'isLocked', {
get: function () {
return this._isLocked;
}
});
Mutex.prototype.tryLock = function () {
if (this._isLocked) {
return false;
}
this._isLocked = true;
return true;
};
Mutex.prototype.unlock = function () {
if (!this._isLocked) {
throw new Error('Mutex is not locked');
}
var waiter = this._waiting.shift();
if (waiter) {
waiter.call(this);
} else {
this._isLocked = false;
}
};
Mutex.prototype.resetQueue = function() {
this._waiting = [];
};
function ReadWriteLock() {
this._isLocked = false;
this._readLocks = 0;
this._waitingToRead = [];
this._waitingToWrite = [];
}
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);
};
Object.defineProperty(ReadWriteLock.prototype, 'isReadLocked', {
get: function () {
return this._isLocked === 'R';
}
});
Object.defineProperty(ReadWriteLock.prototype, 'isWriteLocked', {
get: function () {
return this._isLocked === 'W';
}
});
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');
}
};
function Semaphore(initialCount) {
this._count = initialCount || 1;
this._waiting = [];
}
Semaphore.prototype.wait = function (cb) {
this._count -= 1;
if (this._count < 0) {
this._waiting.push(cb);
} else {
cb.call(this);
}
};
Semaphore.prototype.signal = function () {
this._count += 1;
if (this._count <= 0) {
var waiter = this._waiting.shift();
if (waiter) {
waiter.call(this);
}
}
};
const createCondVariable = function (initialValue) {
return new CondVariable(initialValue);
};
const createSemaphore = function (initialCount) {
return new Semaphore(initialCount);
};
const createMutex = function () {
return new Mutex();
};
const createReadWriteLock = function () {
return new ReadWriteLock();
};
//////////////////////////////////////////////////////////////////////////
////////////////////////////// SAFE PADDING //////////////////////////////
//////////////////////////////////////////////////////////////////////////