@zlattice/lattice-js
Version:
Lattice blockchain TypeScript SDK with dual module support (CJS + ESM)
157 lines • 4.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MutexRW = exports.Mutex = void 0;
function releaseStub() { }
/**
* A simple mutual exclusion lock. It allows you to obtain and release a lock,
* ensuring that only one task can access a critical section at a time.
*/
class Mutex {
constructor() {
this.m_lastPromise = Promise.resolve();
}
/**
* Acquire lock
* @param [bypass=false] option to skip lock acquisition
*/
async obtain(bypass = false) {
let release = releaseStub;
if (bypass)
return release;
const lastPromise = this.m_lastPromise;
this.m_lastPromise = new Promise((resolve) => {
release = resolve;
return resolve;
});
await lastPromise;
return release;
}
/**
* Creates a lock object that can be used with the `using` statement.
* The `using` statement ensures that the lock is released even if an error occurs within the block.
*
* @returns An object with a `Symbol.dispose` method that releases the lock when called.
*
* @example
* ```typescript
* async function main() {
* const mutex = new Mutex();
* {
* using _ = await mutex.lock();
* // Critical section
* // The lock is automatically released when the block exits
* }
* }
* ```
*/
async lock(bypass = false) {
return {
[Symbol.dispose]: await this.obtain(bypass)
};
}
}
exports.Mutex = Mutex;
/**
* A mutual exclusion lock that supports multiple readers or a single writer.
* Readers can obtain a read lock simultaneously, but writers must wait until all readers release the lock.
* It helps in scenarios where you want to optimize concurrent read operations but ensure exclusive write access.
*/
class MutexRW {
constructor() {
this.m_nextRWPromise = Promise.resolve();
this.m_lastRWPromise = Promise.resolve();
this.m_lastROPromise = Promise.resolve();
this.roAccessCnt = 0;
this.rwAccess = false;
}
/**
* Acquire read lock
*/
async obtainRO() {
while (this.rwAccess)
await this.m_lastRWPromise;
++this.roAccessCnt;
let releaseRO = releaseStub;
const thisROPromise = new Promise((resolve) => {
releaseRO = resolve;
return resolve;
});
this.m_lastROPromise = Promise.all([thisROPromise, this.m_lastROPromise]);
thisROPromise.then(() => --this.roAccessCnt);
// Uncomment to detect deadlocks
// const s = new Error().stack;
// Promise.race([thisROPromise, timeout(10000).then(() => true)]).then(
// v => v === true && console.warn('possible deadlock', s),
// );
return releaseRO;
}
/**
* Creates a read lock object that can be used with the `using` statement.
* The `using` statement ensures that the lock is released even if an error occurs within the block.
*
* @returns An object with a `Symbol.dispose` method that releases the lock when called.
*
* @example
* ```typescript
* async function main() {
* const mutex = new MutexRW();
* {
* using _ = await mutex.lockRO();
* // Critical section
* // The lock is automatically released when the block exits
* }
* }
* ```
*/
async lockRO() {
return {
[Symbol.dispose]: await this.obtainRO()
};
}
/**
* Acquire write lock
*/
async obtainRW() {
let releaseRW = releaseStub;
const prevRWPromise = this.m_nextRWPromise;
const thisRWPromise = new Promise((resolve) => {
releaseRW = resolve;
return resolve;
});
this.m_nextRWPromise = thisRWPromise;
await prevRWPromise;
while (this.roAccessCnt)
await this.m_lastROPromise;
this.rwAccess = true;
this.m_lastRWPromise = thisRWPromise;
this.m_lastRWPromise.then(() => {
this.rwAccess = false;
});
return releaseRW;
}
/**
* Creates a write lock object that can be used with the `using` statement.
* The `using` statement ensures that the lock is released even if an error occurs within the block.
*
* @returns An object with a `Symbol.dispose` method that releases the lock when called.
*
* @example
* ```typescript
* async function main() {
* const mutex = new MutexRW();
* {
* using _ = await mutex.lockRW();
* // Critical section
* // The lock is automatically released when the block exits
* }
* }
* ```
*/
async lockRW() {
return {
[Symbol.dispose]: await this.obtainRW()
};
}
}
exports.MutexRW = MutexRW;
//# sourceMappingURL=mutex.js.map