diff --git a/src/Lock.ts b/src/Lock.ts index 6184213..f6161f3 100644 --- a/src/Lock.ts +++ b/src/Lock.ts @@ -24,8 +24,11 @@ class Lock implements Lockable { --this._count; throw e; } + let released = false; return [ async () => { + if (released) return; + released = true; --this._count; release(); // Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54 diff --git a/src/RWLockReader.ts b/src/RWLockReader.ts index 5d7c8d6..b00d216 100644 --- a/src/RWLockReader.ts +++ b/src/RWLockReader.ts @@ -74,8 +74,11 @@ class RWLockReader implements Lockable { // Yield for the first reader to finish locking await yieldMicro(); } + let released= false; return [ async () => { + if (released) return; + released = true; readersRelease = await this.readersLock.acquire(); const readerCount = --this._readerCount; // The last reader unlocks @@ -109,8 +112,11 @@ class RWLockReader implements Lockable { --this._writerCount; throw e; } + let released = false; return [ async () => { + if (released) return; + released = true; release(); --this._writerCount; // Allow semaphore to settle https://github.com/DirtyHairy/async-mutex/issues/54 diff --git a/src/RWLockWriter.ts b/src/RWLockWriter.ts index 766e95c..c6b1f6e 100644 --- a/src/RWLockWriter.ts +++ b/src/RWLockWriter.ts @@ -74,8 +74,11 @@ class RWLockWriter implements Lockable { // Yield for the first reader to finish locking await yieldMicro(); } + let released = false; return [ async () => { + if (released) return; + released = true; const readerCount = --this._readerCount; // The last reader unlocks if (readerCount === 0) { @@ -126,8 +129,11 @@ class RWLockWriter implements Lockable { await yieldMicro(); throw e; } + let released = false; return [ async () => { + if (released) return; + released = true; this.readersRelease(); writersRelease(); --this._writerCount; diff --git a/tests/Lock.test.ts b/tests/Lock.test.ts index 5a176f9..125cb0f 100644 --- a/tests/Lock.test.ts +++ b/tests/Lock.test.ts @@ -212,4 +212,17 @@ describe(Lock.name, () => { await g.next(); await lock.waitForUnlock(100); }); + test('release is idempotent', async () => { + const lock = new Lock(); + let lockAcquire = lock.lock(); + let [lockRelease] = await lockAcquire(); + await lockRelease(); + await lockRelease(); + expect(lock.count).toBe(0); + lockAcquire = lock.lock(); + [lockRelease] = await lockAcquire(); + await lockRelease(); + await lockRelease(); + expect(lock.count).toBe(0); + }); }); diff --git a/tests/RWLockReader.test.ts b/tests/RWLockReader.test.ts index b648958..aada53f 100644 --- a/tests/RWLockReader.test.ts +++ b/tests/RWLockReader.test.ts @@ -484,4 +484,17 @@ describe(RWLockReader.name, () => { await g.next(); await lock.waitForUnlock(100); }); + test('release is idempotent', async () => { + const lock = new RWLockReader(); + let lockAcquire = lock.lock('read'); + let [lockRelease] = await lockAcquire(); + await lockRelease(); + await lockRelease(); + expect(lock.readerCount).toBe(0); + lockAcquire = lock.lock('write'); + [lockRelease] = await lockAcquire(); + await lockRelease(); + await lockRelease(); + expect(lock.writerCount).toBe(0); + }); }); diff --git a/tests/RWLockWriter.test.ts b/tests/RWLockWriter.test.ts index db1c4a2..68ee163 100644 --- a/tests/RWLockWriter.test.ts +++ b/tests/RWLockWriter.test.ts @@ -476,4 +476,17 @@ describe(RWLockWriter.name, () => { await g.next(); await lock.waitForUnlock(100); }); + test('release is idempotent', async () => { + const lock = new RWLockWriter(); + let lockAcquire = lock.lock('read'); + let [lockRelease] = await lockAcquire(); + await lockRelease(); + await lockRelease(); + expect(lock.readerCount).toBe(0); + lockAcquire = lock.lock('write'); + [lockRelease] = await lockAcquire(); + await lockRelease(); + await lockRelease(); + expect(lock.writerCount).toBe(0); + }); });