From 71e00083cdc10c30babc8dec2e6960cd151b445c Mon Sep 17 00:00:00 2001 From: Roger Qiu Date: Fri, 1 Apr 2022 15:35:14 +1100 Subject: [PATCH] WIP --- src/Lock.ts | 9 ++-- src/RWLockReader.ts | 10 ++-- src/RWLockWriter.ts | 26 ++++++---- src/errors.ts | 5 +- tests/Lock.test.ts | 10 ++-- tests/RWLockReader.test.ts | 98 +++++++++++++++++++++----------------- tests/RWLockWriter.test.ts | 98 +++++++++++++++++++++----------------- 7 files changed, 143 insertions(+), 113 deletions(-) diff --git a/src/Lock.ts b/src/Lock.ts index 9f14516..b8f9686 100644 --- a/src/Lock.ts +++ b/src/Lock.ts @@ -33,7 +33,7 @@ class Lock { this, ]; }; - }; + } public get count(): number { return this._count; @@ -47,13 +47,16 @@ class Lock { return this._lock.waitForUnlock(); } - public async withF(f: (resources: [Lock]) => Promise, timeout?: number): Promise { + public async withF( + f: (resources: [Lock]) => Promise, + timeout?: number, + ): Promise { return withF([this.lock(timeout)], f); } public withG( g: (resources: [Lock]) => AsyncGenerator, - timeout?: number + timeout?: number, ): AsyncGenerator { return withG([this.lock(timeout)], g); } diff --git a/src/RWLockReader.ts b/src/RWLockReader.ts index 9de1974..a5a3359 100644 --- a/src/RWLockReader.ts +++ b/src/RWLockReader.ts @@ -50,7 +50,7 @@ class RWLockReader { ++this._writerCount; let lock: MutexInterface = this.lock; if (timeout != null) { - lock = withTimeout(this.lock, timeout, new ErrorAsyncLocksTimeout); + lock = withTimeout(this.lock, timeout, new ErrorAsyncLocksTimeout()); } let release: MutexInterface.Releaser; try { @@ -89,28 +89,28 @@ class RWLockReader { public async withReadF( f: (resources: [RWLockReader]) => Promise, - timeout?: number + timeout?: number, ): Promise { return withF([this.read(timeout)], f); } public async withWriteF( f: (resources: [RWLockReader]) => Promise, - timeout?: number + timeout?: number, ): Promise { return withF([this.write(timeout)], f); } public withReadG( g: (resources: [RWLockReader]) => AsyncGenerator, - timeout?: number + timeout?: number, ): AsyncGenerator { return withG([this.read(timeout)], g); } public withWriteG( g: (resources: [RWLockReader]) => AsyncGenerator, - timeout?: number + timeout?: number, ): AsyncGenerator { return withG([this.write(timeout)], g); } diff --git a/src/RWLockWriter.ts b/src/RWLockWriter.ts index e0af6c2..38c13f0 100644 --- a/src/RWLockWriter.ts +++ b/src/RWLockWriter.ts @@ -26,7 +26,9 @@ class RWLockWriter { let timedOut = false; await Promise.race([ this.writersLock.waitForUnlock(), - sleep(timeout).then(() => { timedOut = true; }) + sleep(timeout).then(() => { + timedOut = true; + }), ]); if (timedOut) { --this.readerCountBlocked; @@ -46,7 +48,7 @@ class RWLockWriter { readersLock = withTimeout( this.readersLock, timeout, - new ErrorAsyncLocksTimeout() + new ErrorAsyncLocksTimeout(), ); } try { @@ -76,7 +78,11 @@ class RWLockWriter { ++this._writerCount; let writersLock: MutexInterface = this.writersLock; if (timeout != null) { - writersLock = withTimeout(this.writersLock, timeout, new ErrorAsyncLocksTimeout()); + writersLock = withTimeout( + this.writersLock, + timeout, + new ErrorAsyncLocksTimeout(), + ); } const t1 = performance.now(); let writersRelease: MutexInterface.Releaser; @@ -89,7 +95,11 @@ class RWLockWriter { let readersLock: MutexInterface = this.readersLock; if (timeout != null) { timeout = timeout - (performance.now() - t1); - readersLock = withTimeout(this.readersLock, timeout, new ErrorAsyncLocksTimeout()); + readersLock = withTimeout( + this.readersLock, + timeout, + new ErrorAsyncLocksTimeout(), + ); } try { this.readersRelease = await readersLock.acquire(); @@ -135,28 +145,28 @@ class RWLockWriter { public async withReadF( f: (resources: [RWLockWriter]) => Promise, - timeout?: number + timeout?: number, ): Promise { return withF([this.read(timeout)], f); } public async withWriteF( f: (resources: [RWLockWriter]) => Promise, - timeout?: number + timeout?: number, ): Promise { return withF([this.write(timeout)], f); } public withReadG( g: (resources: [RWLockWriter]) => AsyncGenerator, - timeout?: number + timeout?: number, ): AsyncGenerator { return withG([this.read(timeout)], g); } public withWriteG( g: (resources: [RWLockWriter]) => AsyncGenerator, - timeout?: number + timeout?: number, ): AsyncGenerator { return withG([this.write(timeout)], g); } diff --git a/src/errors.ts b/src/errors.ts index 1c36323..93c6963 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -16,7 +16,4 @@ class ErrorAsyncLocks extends CustomError { class ErrorAsyncLocksTimeout extends ErrorAsyncLocks {} -export { - ErrorAsyncLocks, - ErrorAsyncLocksTimeout -}; +export { ErrorAsyncLocks, ErrorAsyncLocksTimeout }; diff --git a/tests/Lock.test.ts b/tests/Lock.test.ts index 8ee1b32..0a5b753 100644 --- a/tests/Lock.test.ts +++ b/tests/Lock.test.ts @@ -165,7 +165,9 @@ describe(Lock.name, () => { expect(lock.isLocked()).toBe(true); expect(lock.count).toBe(1); const f = jest.fn(); - await expect(withF([lock.lock(100)], f)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(withF([lock.lock(100)], f)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); expect(lock.isLocked()).toBe(true); expect(lock.count).toBe(1); @@ -174,10 +176,12 @@ describe(Lock.name, () => { expect(lock.count).toBe(0); await lock.withF(async () => { const f = jest.fn(); - await expect(lock.withF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); - const g = lock.withG(async function *() { + const g = lock.withG(async function* () { expect(lock.isLocked()).toBe(true); expect(lock.count).toBe(1); const f = jest.fn(); diff --git a/tests/RWLockReader.test.ts b/tests/RWLockReader.test.ts index 6e347bb..4299479 100644 --- a/tests/RWLockReader.test.ts +++ b/tests/RWLockReader.test.ts @@ -33,23 +33,24 @@ describe(RWLockReader.name, () => { }); test('withG on read', async () => { const lock = new RWLockReader(); - const g1 = withG( - [lock.read()], - async function* ([lock]): AsyncGenerator { - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(1); - expect(lock.writerCount).toBe(0); - yield 'first'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(1); - expect(lock.writerCount).toBe(0); - yield 'second'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(1); - expect(lock.writerCount).toBe(0); - return 'last'; - }, - ); + const g1 = withG([lock.read()], async function* ([lock]): AsyncGenerator< + string, + string, + void + > { + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(1); + expect(lock.writerCount).toBe(0); + yield 'first'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(1); + expect(lock.writerCount).toBe(0); + yield 'second'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(1); + expect(lock.writerCount).toBe(0); + return 'last'; + }); for await (const _ of g1) { // It should be locked during iteration expect(lock.isLocked()).toBe(true); @@ -107,23 +108,24 @@ describe(RWLockReader.name, () => { }); test('withG on write', async () => { const lock = new RWLockReader(); - const g1 = withG( - [lock.write()], - async function* ([lock]): AsyncGenerator { - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(0); - expect(lock.writerCount).toBe(1); - yield 'first'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(0); - expect(lock.writerCount).toBe(1); - yield 'second'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(0); - expect(lock.writerCount).toBe(1); - return 'last'; - }, - ); + const g1 = withG([lock.write()], async function* ([lock]): AsyncGenerator< + string, + string, + void + > { + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(0); + expect(lock.writerCount).toBe(1); + yield 'first'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(0); + expect(lock.writerCount).toBe(1); + yield 'second'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(0); + expect(lock.writerCount).toBe(1); + return 'last'; + }); for await (const _ of g1) { // It should be locked during iteration expect(lock.isLocked()).toBe(true); @@ -358,9 +360,9 @@ describe(RWLockReader.name, () => { expect(lock.readerCount).toBe(1); expect(lock.writerCount).toBe(0); const f = jest.fn(); - await expect( - withF([lock.write(100)], f) - ).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(withF([lock.write(100)], f)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(1); @@ -374,9 +376,9 @@ describe(RWLockReader.name, () => { expect(lock.readerCount).toBe(0); expect(lock.writerCount).toBe(1); const f = jest.fn(); - await expect( - withF([lock.read(100)], f) - ).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(withF([lock.read(100)], f)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(0); @@ -384,20 +386,26 @@ describe(RWLockReader.name, () => { }); await lock.withReadF(async () => { const f = jest.fn(); - await expect(lock.withWriteF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withWriteF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); await lock.withWriteF(async () => { const f = jest.fn(); - await expect(lock.withReadF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withReadF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); await lock.withWriteF(async () => { const f = jest.fn(); - await expect(lock.withWriteF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withWriteF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); - const gRead = lock.withReadG(async function *() { + const gRead = lock.withReadG(async function* () { expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(1); expect(lock.writerCount).toBe(0); @@ -413,7 +421,7 @@ describe(RWLockReader.name, () => { expect(lock.isLocked()).toBe(false); expect(lock.readerCount).toBe(0); expect(lock.writerCount).toBe(0); - const gWrite = lock.withWriteG(async function *() { + const gWrite = lock.withWriteG(async function* () { expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(0); expect(lock.writerCount).toBe(1); diff --git a/tests/RWLockWriter.test.ts b/tests/RWLockWriter.test.ts index c2aaf40..21a5afd 100644 --- a/tests/RWLockWriter.test.ts +++ b/tests/RWLockWriter.test.ts @@ -33,23 +33,24 @@ describe(RWLockWriter.name, () => { }); test('withG on read', async () => { const lock = new RWLockWriter(); - const g1 = withG( - [lock.read()], - async function* ([lock]): AsyncGenerator { - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(1); - expect(lock.writerCount).toBe(0); - yield 'first'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(1); - expect(lock.writerCount).toBe(0); - yield 'second'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(1); - expect(lock.writerCount).toBe(0); - return 'last'; - }, - ); + const g1 = withG([lock.read()], async function* ([lock]): AsyncGenerator< + string, + string, + void + > { + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(1); + expect(lock.writerCount).toBe(0); + yield 'first'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(1); + expect(lock.writerCount).toBe(0); + yield 'second'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(1); + expect(lock.writerCount).toBe(0); + return 'last'; + }); for await (const _ of g1) { // It should be locked during iteration expect(lock.isLocked()).toBe(true); @@ -107,23 +108,24 @@ describe(RWLockWriter.name, () => { }); test('withG on write', async () => { const lock = new RWLockWriter(); - const g1 = withG( - [lock.write()], - async function* ([lock]): AsyncGenerator { - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(0); - expect(lock.writerCount).toBe(1); - yield 'first'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(0); - expect(lock.writerCount).toBe(1); - yield 'second'; - expect(lock.isLocked()).toBe(true); - expect(lock.readerCount).toBe(0); - expect(lock.writerCount).toBe(1); - return 'last'; - }, - ); + const g1 = withG([lock.write()], async function* ([lock]): AsyncGenerator< + string, + string, + void + > { + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(0); + expect(lock.writerCount).toBe(1); + yield 'first'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(0); + expect(lock.writerCount).toBe(1); + yield 'second'; + expect(lock.isLocked()).toBe(true); + expect(lock.readerCount).toBe(0); + expect(lock.writerCount).toBe(1); + return 'last'; + }); for await (const _ of g1) { // It should be locked during iteration expect(lock.isLocked()).toBe(true); @@ -358,9 +360,9 @@ describe(RWLockWriter.name, () => { expect(lock.readerCount).toBe(1); expect(lock.writerCount).toBe(0); const f = jest.fn(); - await expect( - withF([lock.write(100)], f) - ).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(withF([lock.write(100)], f)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(1); @@ -374,9 +376,9 @@ describe(RWLockWriter.name, () => { expect(lock.readerCount).toBe(0); expect(lock.writerCount).toBe(1); const f = jest.fn(); - await expect( - withF([lock.read(100)], f) - ).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(withF([lock.read(100)], f)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(0); @@ -384,20 +386,26 @@ describe(RWLockWriter.name, () => { }); await lock.withReadF(async () => { const f = jest.fn(); - await expect(lock.withWriteF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withWriteF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); await lock.withWriteF(async () => { const f = jest.fn(); - await expect(lock.withReadF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withReadF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); await lock.withWriteF(async () => { const f = jest.fn(); - await expect(lock.withWriteF(f, 100)).rejects.toThrow(errors.ErrorAsyncLocksTimeout); + await expect(lock.withWriteF(f, 100)).rejects.toThrow( + errors.ErrorAsyncLocksTimeout, + ); expect(f).not.toBeCalled(); }, 100); - const gRead = lock.withReadG(async function *() { + const gRead = lock.withReadG(async function* () { expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(1); expect(lock.writerCount).toBe(0); @@ -410,7 +418,7 @@ describe(RWLockWriter.name, () => { expect(lock.writerCount).toBe(0); }); await gRead.next(); - const gWrite = lock.withWriteG(async function *() { + const gWrite = lock.withWriteG(async function* () { expect(lock.isLocked()).toBe(true); expect(lock.readerCount).toBe(0); expect(lock.writerCount).toBe(1);