@matrixai/async-locks
Advanced tools
Comparing version 2.2.5 to 2.3.0
@@ -8,3 +8,3 @@ import type { ResourceAcquire } from '@matrixai/resources'; | ||
get count(): number; | ||
isLocked(key?: ToString): boolean; | ||
isLocked(key?: ToString, ...params: Parameters<L['isLocked']>): boolean; | ||
waitForUnlock(timeout?: number, key?: ToString): Promise<void>; | ||
@@ -11,0 +11,0 @@ withF<T>(...params: [ |
@@ -81,6 +81,6 @@ "use strict"; | ||
} | ||
isLocked(key) { | ||
isLocked(key, ...params) { | ||
if (key == null) { | ||
for (const lock of this._locks.values()) { | ||
if (lock.isLocked()) | ||
if (lock.isLocked(...params)) | ||
return true; | ||
@@ -94,3 +94,3 @@ } | ||
return false; | ||
return lock.isLocked(); | ||
return lock.isLocked(...params); | ||
} | ||
@@ -97,0 +97,0 @@ } |
@@ -9,6 +9,9 @@ import type { MutexInterface } from 'async-mutex'; | ||
declare class RWLockReader implements Lockable { | ||
protected readersLock: Mutex; | ||
protected writersLock: Mutex; | ||
protected writersRelease: MutexInterface.Releaser; | ||
protected readerCountBlocked: number; | ||
protected _readerCount: number; | ||
protected _writerCount: number; | ||
protected _lock: Mutex; | ||
protected release: MutexInterface.Releaser; | ||
protected activeLock: 'read' | 'write' | null; | ||
lock(type: 'read' | 'write', timeout?: number): ResourceAcquire<RWLockReader>; | ||
@@ -20,3 +23,7 @@ read(timeout?: number): ResourceAcquire<RWLockReader>; | ||
get writerCount(): number; | ||
isLocked(): boolean; | ||
/** | ||
* Check if locked | ||
* If passed `type`, it will also check that the active lock is of that type | ||
*/ | ||
isLocked(type?: 'read' | 'write'): boolean; | ||
waitForUnlock(timeout?: number): Promise<void>; | ||
@@ -23,0 +30,0 @@ withF<T>(...params: [ |
@@ -12,5 +12,8 @@ "use strict"; | ||
constructor() { | ||
this.readersLock = new async_mutex_1.Mutex(); | ||
this.writersLock = new async_mutex_1.Mutex(); | ||
this.readerCountBlocked = 0; | ||
this._readerCount = 0; | ||
this._writerCount = 0; | ||
this._lock = new async_mutex_1.Mutex(); | ||
this.activeLock = null; | ||
} | ||
@@ -27,18 +30,39 @@ lock(type, timeout) { | ||
return async () => { | ||
const t1 = performance.now(); | ||
++this.readerCountBlocked; | ||
let readersLock = this.readersLock; | ||
if (timeout != null) { | ||
readersLock = (0, async_mutex_1.withTimeout)(this.readersLock, timeout, new errors_1.ErrorAsyncLocksTimeout()); | ||
} | ||
let readersRelease; | ||
try { | ||
readersRelease = await readersLock.acquire(); | ||
} | ||
catch (e) { | ||
--this.readerCountBlocked; | ||
throw e; | ||
} | ||
--this.readerCountBlocked; | ||
const readerCount = ++this._readerCount; | ||
// The first reader locks | ||
if (readerCount === 1) { | ||
let lock = this._lock; | ||
let writersLock = this.writersLock; | ||
if (timeout != null) { | ||
lock = (0, async_mutex_1.withTimeout)(this._lock, timeout, new errors_1.ErrorAsyncLocksTimeout()); | ||
timeout = timeout - (performance.now() - t1); | ||
writersLock = (0, async_mutex_1.withTimeout)(this.writersLock, timeout, new errors_1.ErrorAsyncLocksTimeout()); | ||
} | ||
try { | ||
this.release = await lock.acquire(); | ||
this.writersRelease = await writersLock.acquire(); | ||
} | ||
catch (e) { | ||
readersRelease(); | ||
--this._readerCount; | ||
throw e; | ||
} | ||
readersRelease(); | ||
this.activeLock = 'read'; | ||
} | ||
else { | ||
readersRelease(); | ||
this.activeLock = 'read'; | ||
// Yield for the first reader to finish locking | ||
@@ -49,7 +73,10 @@ await (0, utils_1.yieldMicro)(); | ||
async () => { | ||
readersRelease = await this.readersLock.acquire(); | ||
const readerCount = --this._readerCount; | ||
// The last reader unlocks | ||
if (readerCount === 0) { | ||
this.release(); | ||
this.writersRelease(); | ||
} | ||
readersRelease(); | ||
this.activeLock = null; | ||
// Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54 | ||
@@ -65,9 +92,9 @@ await (0, utils_1.yieldMicro)(); | ||
++this._writerCount; | ||
let lock = this._lock; | ||
let writersLock = this.writersLock; | ||
if (timeout != null) { | ||
lock = (0, async_mutex_1.withTimeout)(this._lock, timeout, new errors_1.ErrorAsyncLocksTimeout()); | ||
writersLock = (0, async_mutex_1.withTimeout)(this.writersLock, timeout, new errors_1.ErrorAsyncLocksTimeout()); | ||
} | ||
let release; | ||
try { | ||
release = await lock.acquire(); | ||
release = await writersLock.acquire(); | ||
} | ||
@@ -78,2 +105,3 @@ catch (e) { | ||
} | ||
this.activeLock = 'write'; | ||
return [ | ||
@@ -83,2 +111,3 @@ async () => { | ||
--this._writerCount; | ||
this.activeLock = null; | ||
// Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54 | ||
@@ -95,3 +124,3 @@ await (0, utils_1.yieldMicro)(); | ||
get readerCount() { | ||
return this._readerCount; | ||
return this._readerCount + this.readerCountBlocked; | ||
} | ||
@@ -101,4 +130,14 @@ get writerCount() { | ||
} | ||
isLocked() { | ||
return this._lock.isLocked(); | ||
/** | ||
* Check if locked | ||
* If passed `type`, it will also check that the active lock is of that type | ||
*/ | ||
isLocked(type) { | ||
if (type != null) { | ||
return (this.activeLock === type && | ||
(this.readersLock.isLocked() || this.writersLock.isLocked())); | ||
} | ||
else { | ||
return this.readersLock.isLocked() || this.writersLock.isLocked(); | ||
} | ||
} | ||
@@ -109,3 +148,6 @@ async waitForUnlock(timeout) { | ||
await Promise.race([ | ||
this._lock.waitForUnlock(), | ||
Promise.all([ | ||
this.readersLock.waitForUnlock(), | ||
this.writersLock.waitForUnlock(), | ||
]), | ||
(0, utils_1.sleep)(timeout).then(() => { | ||
@@ -120,3 +162,6 @@ timedOut = true; | ||
else { | ||
await this._lock.waitForUnlock(); | ||
await Promise.all([ | ||
this.readersLock.waitForUnlock(), | ||
this.writersLock.waitForUnlock(), | ||
]); | ||
} | ||
@@ -123,0 +168,0 @@ } |
@@ -15,2 +15,3 @@ import type { MutexInterface } from 'async-mutex'; | ||
protected _writerCount: number; | ||
protected activeLock: 'read' | 'write' | null; | ||
lock(type: 'read' | 'write', timeout?: number): ResourceAcquire<RWLockWriter>; | ||
@@ -22,3 +23,7 @@ read(timeout?: number): ResourceAcquire<RWLockWriter>; | ||
get writerCount(): number; | ||
isLocked(): boolean; | ||
/** | ||
* Check if locked | ||
* If passed `type`, it will also check that the active lock is of that type | ||
*/ | ||
isLocked(type?: 'read' | 'write'): boolean; | ||
waitForUnlock(timeout?: number): Promise<void>; | ||
@@ -25,0 +30,0 @@ withF<T>(...params: [ |
@@ -18,2 +18,3 @@ "use strict"; | ||
this._writerCount = 0; | ||
this.activeLock = null; | ||
} | ||
@@ -66,4 +67,6 @@ lock(type, timeout) { | ||
} | ||
this.activeLock = 'read'; | ||
} | ||
else { | ||
this.activeLock = 'read'; | ||
// Yield for the first reader to finish locking | ||
@@ -78,2 +81,3 @@ await (0, utils_1.yieldMicro)(); | ||
this.readersRelease(); | ||
this.activeLock = null; | ||
// Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54 | ||
@@ -118,2 +122,3 @@ await (0, utils_1.yieldMicro)(); | ||
} | ||
this.activeLock = 'write'; | ||
return [ | ||
@@ -124,2 +129,3 @@ async () => { | ||
--this._writerCount; | ||
this.activeLock = null; | ||
// Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54 | ||
@@ -141,4 +147,14 @@ await (0, utils_1.yieldMicro)(); | ||
} | ||
isLocked() { | ||
return this.readersLock.isLocked() || this.writersLock.isLocked(); | ||
/** | ||
* Check if locked | ||
* If passed `type`, it will also check that the active lock is of that type | ||
*/ | ||
isLocked(type) { | ||
if (type != null) { | ||
return (this.activeLock === type && | ||
(this.readersLock.isLocked() || this.writersLock.isLocked())); | ||
} | ||
else { | ||
return this.readersLock.isLocked() || this.writersLock.isLocked(); | ||
} | ||
} | ||
@@ -145,0 +161,0 @@ async waitForUnlock(timeout) { |
@@ -17,3 +17,3 @@ import type { ResourceAcquire } from '@matrixai/resources'; | ||
lock(...params: Array<unknown>): ResourceAcquire<Lockable>; | ||
isLocked(): boolean; | ||
isLocked(...params: Array<unknown>): boolean; | ||
waitForUnlock(timeout?: number): Promise<void>; | ||
@@ -20,0 +20,0 @@ withF<T>(...params: Array<unknown>): Promise<T>; |
{ | ||
"name": "@matrixai/async-locks", | ||
"version": "2.2.5", | ||
"version": "2.3.0", | ||
"author": "Roger Qiu", | ||
@@ -15,3 +15,4 @@ "description": "Asynchronous locking utilities", | ||
"prepare": "tsc -p ./tsconfig.build.json", | ||
"build": "rm -r ./dist || true; tsc -p ./tsconfig.build.json", | ||
"build": "rimraf ./dist && tsc -p ./tsconfig.build.json", | ||
"postversion": "npm install --package-lock-only --ignore-scripts --silent", | ||
"ts-node": "ts-node -r tsconfig-paths/register", | ||
@@ -21,3 +22,3 @@ "test": "jest", | ||
"lintfix": "eslint '{src,tests}/**/*.{js,ts}' --fix", | ||
"docs": "rm -r ./docs || true; typedoc --gitRevision master --tsconfig ./tsconfig.build.json --out ./docs src" | ||
"docs": "rimraf ./docs && typedoc --gitRevision master --tsconfig ./tsconfig.build.json --out ./docs src" | ||
}, | ||
@@ -39,3 +40,5 @@ "dependencies": { | ||
"jest": "^27.2.5", | ||
"jest-junit": "^13.2.0", | ||
"prettier": "^2.6.2", | ||
"rimraf": "^3.0.2", | ||
"ts-jest": "^27.0.5", | ||
@@ -42,0 +45,0 @@ "ts-node": "^10.4.0", |
# js-async-locks | ||
[![pipeline status](https://gitlab.com/MatrixAI/open-source/js-async-locks/badges/master/pipeline.svg)](https://gitlab.com/MatrixAI/open-source/js-async-locks/commits/master) | ||
staging:[![pipeline status](https://gitlab.com/MatrixAI/open-source/js-async-locks/badges/staging/pipeline.svg)](https://gitlab.com/MatrixAI/open-source/js-async-locks/commits/staging) | ||
master:[![pipeline status](https://gitlab.com/MatrixAI/open-source/js-async-locks/badges/master/pipeline.svg)](https://gitlab.com/MatrixAI/open-source/js-async-locks/commits/master) | ||
@@ -5,0 +6,0 @@ Asynchronous lock utilities. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
64698
857
51
17