read-write-mutexify
Advanced tools
Comparing version 1.0.0 to 2.0.0
132
index.js
@@ -1,60 +0,120 @@ | ||
module.exports = class ReadWriteLock { | ||
constructor () { | ||
this.destroyed = false | ||
class WriteLock { | ||
constructor (parent) { | ||
this.writing = false | ||
this.readers = 0 | ||
this.waitingReads = [] | ||
this.waitingWrites = [] | ||
this._waiting = [] | ||
this._parent = parent | ||
this._wait = pushToQueue.bind(this, this._waiting) | ||
} | ||
this._waitForWriteLockBound = pushToQueue.bind(this, this.waitingWrites) | ||
this._waitForReadLockBound = pushToQueue.bind(this, this.waitingReads) | ||
this._releaseWriteLockBound = this._releaseWriteLock.bind(this) | ||
this._releaseReadLockBound = this._releaseReadLock.bind(this) | ||
get locked () { | ||
return this.writing || this._parent.read.readers > 0 | ||
} | ||
write () { | ||
if (this.writing === false && this.readers === 0) { | ||
lock () { | ||
if (this._parent._destroying) { | ||
return Promise.reject(this._parent._destroyError) | ||
} | ||
if (this.writing === false && this._parent.read.readers === 0) { | ||
this.writing = true | ||
return Promise.resolve(this._releaseWriteLockBound) | ||
return Promise.resolve() | ||
} | ||
return new Promise(this._waitForWriteLockBound) | ||
return new Promise(this._wait) | ||
} | ||
read () { | ||
if (this.writing === false) { | ||
this.readers++ | ||
return Promise.resolve(this._releaseReadLockBound) | ||
unlock () { | ||
this.writing = false | ||
this._parent._bump() | ||
} | ||
async flush () { | ||
if (this.writing === false) return | ||
try { | ||
await this.lock() | ||
} catch { | ||
return | ||
} | ||
this.unlock() | ||
} | ||
} | ||
return new Promise(this._waitForReadLockBound) | ||
class ReadLock { | ||
constructor (parent) { | ||
this.readers = 0 | ||
this._waiting = [] | ||
this._parent = parent | ||
this._wait = pushToQueue.bind(this, this._waiting) | ||
} | ||
destroy () { | ||
if (this.destroyed) return | ||
this.destroyed = true | ||
while (this.waitingReads.length) this.waitingReads.shift()[1](new Error('Lock destroyed')) | ||
while (this.waitingWrites.length) this.waitingWrites.shift()[1](new Error('Lock destroyed')) | ||
get locked () { | ||
return this._parent.writing | ||
} | ||
_releaseWriteLock () { | ||
this.writing = false | ||
this._bump() | ||
lock () { | ||
if (this._parent._destroying) { | ||
return Promise.reject(this._parent._destroyError) | ||
} | ||
if (this._parent.write.writing === false) { | ||
this.readers++ | ||
return Promise.resolve() | ||
} | ||
return new Promise(this._wait) | ||
} | ||
_releaseReadLock () { | ||
unlock () { | ||
this.readers-- | ||
this._bump() | ||
this._parent._bump() | ||
} | ||
async flush () { | ||
if (this.writing === false) return | ||
try { | ||
await this.lock() | ||
} catch { | ||
return | ||
} | ||
this.unlock() | ||
} | ||
} | ||
module.exports = class ReadWriteLock { | ||
constructor () { | ||
this.read = new ReadLock(this) | ||
this.write = new WriteLock(this) | ||
this._destroyError = null | ||
this._destroying = null | ||
} | ||
get destroyed () { | ||
return !!this._destroying | ||
} | ||
destroy (err) { | ||
if (this._destroying) return this._destroying | ||
this._destroying = Promise.all([this.read.flush(), this.write.flush()]) | ||
this._destroyError = err || new Error('Mutex has been destroyed') | ||
if (err) { | ||
while (this.read._waiting) this._waiting.shift()[1](err) | ||
while (this.write._waiting) this._waiting.shift()[1](err) | ||
} | ||
return this._destroying | ||
} | ||
_bump () { | ||
if (this.writing === false && this.readers === 0 && this.waitingWrites.length > 0) { | ||
this.writing = true | ||
this.waitingWrites.shift()[0](this._releaseWriteLockBound) | ||
if (this.write.writing === false && this.read.readers === 0 && this.write._waiting.length > 0) { | ||
this.write.writing = true | ||
this.write._waiting.shift()[0]() | ||
} | ||
while (this.writing === false && this.waitingReads.length > 0) { | ||
this.readers++ | ||
this.waitingReads.shift()[0](this._releaseReadLockBound) | ||
while (this.write.writing === false && this.read._waiting.length > 0) { | ||
this.read.readers++ | ||
this.read._waiting.shift()[0]() | ||
} | ||
@@ -61,0 +121,0 @@ } |
{ | ||
"name": "read-write-mutexify", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "Like mutexify but with read/write locks", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -14,12 +14,19 @@ # read-write-mutexify | ||
const lock = new RW() | ||
const rw = new RW() | ||
// read locks waits for writer locks to be released | ||
const release1 = await lock.read() | ||
const release2 = await lock.read() // make as many as you want | ||
await rw.read.lock() | ||
await rw.read.lock() // make as many as you want | ||
// unlock with unlock | ||
rw.read.unlock() | ||
rw.read.unlock() | ||
// only one writer can have the write lock and it waits | ||
// for any read lock to be released | ||
const release = await lock.writer() | ||
await rw.write.lock() | ||
// unlock with unlock | ||
rw.write.unlock() | ||
``` | ||
@@ -26,0 +33,0 @@ |
38
test.js
@@ -5,11 +5,11 @@ const test = require('brittle') | ||
test('as many reads as you want', async function (t) { | ||
const lock = new RW() | ||
const rw = new RW() | ||
const r1 = await lock.read() | ||
const r2 = await lock.read() | ||
await rw.read.lock() | ||
await rw.read.lock() | ||
t.pass('didnt dead lock') | ||
r1() | ||
r2() | ||
rw.read.unlock() | ||
rw.read.unlock() | ||
}) | ||
@@ -20,8 +20,8 @@ | ||
const lock = new RW() | ||
const rw = new RW() | ||
let released = false | ||
const release = await lock.write() | ||
await rw.write.lock() | ||
lock.write().then(() => { | ||
rw.write.lock().then(() => { | ||
t.ok(released, 'only one writer active') | ||
@@ -32,3 +32,3 @@ }) | ||
release() | ||
rw.write.unlock() | ||
released = true | ||
@@ -40,8 +40,8 @@ }) | ||
const lock = new RW() | ||
const rw = new RW() | ||
let released = false | ||
const release = await lock.write() | ||
await rw.write.lock() | ||
lock.read().then(() => { | ||
rw.read.lock().then(() => { | ||
t.ok(released, 'read waited for writer') | ||
@@ -52,3 +52,3 @@ }) | ||
release() | ||
rw.write.unlock() | ||
released = true | ||
@@ -60,9 +60,9 @@ }) | ||
const lock = new RW() | ||
const rw = new RW() | ||
let released = false | ||
const r1 = await lock.read() | ||
const r2 = await lock.read() | ||
await rw.read.lock() | ||
await rw.read.lock() | ||
lock.write().then(() => { | ||
rw.write.lock().then(() => { | ||
t.ok(released, 'write waited for all readers') | ||
@@ -73,8 +73,8 @@ }) | ||
r1() | ||
rw.read.unlock() | ||
await new Promise(resolve => setImmediate(resolve)) | ||
r2() | ||
rw.read.unlock() | ||
released = true | ||
}) |
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
6603
151
36