@matrixai/async-init
Advanced tools
Comparing version 1.4.0 to 1.5.0
@@ -11,11 +11,16 @@ import type { MutexInterface } from 'async-mutex'; | ||
declare const initLock: unique symbol; | ||
/** | ||
* Single threaded write-preferring read write lock | ||
*/ | ||
declare class RWLock { | ||
protected readersLock: Mutex; | ||
protected writersLock: Mutex; | ||
protected readersRelease: MutexInterface.Releaser; | ||
protected readerCountBlocked: number; | ||
protected _readerCount: number; | ||
protected _writerCount: number; | ||
protected lock: Mutex; | ||
protected release: MutexInterface.Releaser; | ||
get readerCount(): number; | ||
get writerCount(): number; | ||
read<T>(f: () => Promise<T>): Promise<T>; | ||
write<T>(f: () => Promise<T>): Promise<T>; | ||
withRead<T>(f: () => Promise<T>): Promise<T>; | ||
withWrite<T>(f: () => Promise<T>): Promise<T>; | ||
acquireRead(): Promise<() => void>; | ||
@@ -22,0 +27,0 @@ acquireWrite(): Promise<() => void>; |
@@ -18,10 +18,15 @@ "use strict"; | ||
exports.initLock = initLock; | ||
/** | ||
* Single threaded write-preferring read write lock | ||
*/ | ||
class RWLock { | ||
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(); | ||
} | ||
get readerCount() { | ||
return this._readerCount; | ||
return this._readerCount + this.readerCountBlocked; | ||
} | ||
@@ -31,8 +36,4 @@ get writerCount() { | ||
} | ||
async read(f) { | ||
let readerCount = ++this._readerCount; | ||
// The first reader locks | ||
if (readerCount === 1) { | ||
this.release = await this.lock.acquire(); | ||
} | ||
async withRead(f) { | ||
const release = await this.acquireRead(); | ||
try { | ||
@@ -42,12 +43,7 @@ return await f(); | ||
finally { | ||
readerCount = --this._readerCount; | ||
// The last reader unlocks | ||
if (readerCount === 0) { | ||
this.release(); | ||
} | ||
release(); | ||
} | ||
} | ||
async write(f) { | ||
this.release = await this.lock.acquire(); | ||
++this._writerCount; | ||
async withWrite(f) { | ||
const release = await this.acquireWrite(); | ||
try { | ||
@@ -57,11 +53,15 @@ return await f(); | ||
finally { | ||
--this._writerCount; | ||
this.release(); | ||
release(); | ||
} | ||
} | ||
async acquireRead() { | ||
if (this._writerCount > 0) { | ||
++this.readerCountBlocked; | ||
await this.writersLock.waitForUnlock(); | ||
--this.readerCountBlocked; | ||
} | ||
const readerCount = ++this._readerCount; | ||
// The first reader locks | ||
if (readerCount === 1) { | ||
this.release = await this.lock.acquire(); | ||
this.readersRelease = await this.readersLock.acquire(); | ||
} | ||
@@ -72,3 +72,3 @@ return () => { | ||
if (readerCount === 0) { | ||
this.release(); | ||
this.readersRelease(); | ||
} | ||
@@ -78,14 +78,20 @@ }; | ||
async acquireWrite() { | ||
this.release = await this.lock.acquire(); | ||
++this._writerCount; | ||
const writersRelease = await this.writersLock.acquire(); | ||
this.readersRelease = await this.readersLock.acquire(); | ||
return () => { | ||
this.readersRelease(); | ||
writersRelease(); | ||
--this._writerCount; | ||
this.release(); | ||
}; | ||
} | ||
isLocked() { | ||
return this.lock.isLocked(); | ||
return this.readersLock.isLocked() || this.writersLock.isLocked(); | ||
} | ||
async waitForUnlock() { | ||
return this.lock.waitForUnlock(); | ||
await Promise.all([ | ||
this.readersLock.waitForUnlock(), | ||
this.writersLock.waitForUnlock(), | ||
]); | ||
return; | ||
} | ||
@@ -92,0 +98,0 @@ } |
{ | ||
"name": "@matrixai/async-init", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"author": "Roger Qiu", | ||
@@ -5,0 +5,0 @@ "description": "Asynchronous Initialisation and Deinitialisation Decorators", |
@@ -93,6 +93,4 @@ # js-async-init | ||
Note that it is unsafe to call the create, destroy, start and stop methods concurrently with other method calls. There is no concurrency control. Instead a simple method of making the methods idempotent is used. | ||
The `start`, `stop`, and `destroy` calls are all concurrent-controlled with `RWLock`. They are idempotent and they are mutually exclusive between each other and any blocking `ready` decorated methods. Decorated methods can block `start`, `stop`, and `destroy`, but share a read lock between each other. | ||
Note that `start`, `stop` and `destroy` are idempotent. This means repeated calls are noops. | ||
Refer to https://gist.github.com/CMCDragonkai/1dbf5069d9efc11585c27cc774271584 for further the motivation of this library. | ||
@@ -99,0 +97,0 @@ |
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
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
59932
735
140