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 ////////////////////////////// //////////////////////////////////////////////////////////////////////////