Skip to content

Commit c3f450a

Browse files
committed
Enabled timeout for waitForUnlock
1 parent b810928 commit c3f450a

File tree

6 files changed

+104
-12
lines changed

6 files changed

+104
-12
lines changed

src/Lock.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { MutexInterface } from 'async-mutex';
22
import type { ResourceAcquire } from '@matrixai/resources';
33
import { Mutex, withTimeout } from 'async-mutex';
44
import { withF, withG } from '@matrixai/resources';
5-
import { yieldMicro } from './utils';
5+
import { sleep, yieldMicro } from './utils';
66
import { ErrorAsyncLocksTimeout } from './errors';
77

88
class Lock {
@@ -43,8 +43,21 @@ class Lock {
4343
return this._lock.isLocked();
4444
}
4545

46-
public async waitForUnlock(): Promise<void> {
47-
return this._lock.waitForUnlock();
46+
public async waitForUnlock(timeout?: number): Promise<void> {
47+
if (timeout != null) {
48+
let timedOut = false;
49+
await Promise.race([
50+
this._lock.waitForUnlock(),
51+
sleep(timeout).then(() => {
52+
timedOut = true;
53+
}),
54+
]);
55+
if (timedOut) {
56+
throw new ErrorAsyncLocksTimeout();
57+
}
58+
} else {
59+
await this._lock.waitForUnlock();
60+
}
4861
}
4962

5063
public async withF<T>(

src/RWLockReader.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { MutexInterface } from 'async-mutex';
22
import type { ResourceAcquire } from '@matrixai/resources';
33
import { Mutex, withTimeout } from 'async-mutex';
44
import { withF, withG } from '@matrixai/resources';
5-
import { yieldMicro } from './utils';
5+
import { sleep, yieldMicro } from './utils';
66
import { ErrorAsyncLocksTimeout } from './errors';
77

88
/**
@@ -83,8 +83,21 @@ class RWLockReader {
8383
return this.lock.isLocked();
8484
}
8585

86-
public async waitForUnlock(): Promise<void> {
87-
return this.lock.waitForUnlock();
86+
public async waitForUnlock(timeout?: number): Promise<void> {
87+
if (timeout != null) {
88+
let timedOut = false;
89+
await Promise.race([
90+
this.lock.waitForUnlock(),
91+
sleep(timeout).then(() => {
92+
timedOut = true;
93+
}),
94+
]);
95+
if (timedOut) {
96+
throw new ErrorAsyncLocksTimeout();
97+
}
98+
} else {
99+
await this.lock.waitForUnlock();
100+
}
88101
}
89102

90103
public async withReadF<T>(

src/RWLockWriter.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,27 @@ class RWLockWriter {
135135
return this.readersLock.isLocked() || this.writersLock.isLocked();
136136
}
137137

138-
public async waitForUnlock(): Promise<void> {
139-
await Promise.all([
140-
this.readersLock.waitForUnlock(),
141-
this.writersLock.waitForUnlock(),
142-
]);
143-
return;
138+
public async waitForUnlock(timeout?: number): Promise<void> {
139+
if (timeout != null) {
140+
let timedOut = false;
141+
await Promise.race([
142+
Promise.all([
143+
this.readersLock.waitForUnlock(),
144+
this.writersLock.waitForUnlock(),
145+
]),
146+
sleep(timeout).then(() => {
147+
timedOut = true;
148+
}),
149+
]);
150+
if (timedOut) {
151+
throw new ErrorAsyncLocksTimeout();
152+
}
153+
} else {
154+
await Promise.all([
155+
this.readersLock.waitForUnlock(),
156+
this.writersLock.waitForUnlock(),
157+
]);
158+
}
144159
}
145160

146161
public async withReadF<T>(

tests/Lock.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,21 @@ describe(Lock.name, () => {
195195
expect(lock.isLocked()).toBe(false);
196196
expect(lock.count).toBe(0);
197197
});
198+
test('timeout waiting for unlock', async () => {
199+
const lock = new Lock();
200+
await lock.waitForUnlock(100);
201+
await withF([lock.lock()], async ([lock]) => {
202+
await expect(lock.waitForUnlock(100)).rejects.toThrow(
203+
errors.ErrorAsyncLocksTimeout,
204+
);
205+
});
206+
await lock.waitForUnlock(100);
207+
const g = withG([lock.lock()], async function* ([lock]) {
208+
await expect(lock.waitForUnlock(100)).rejects.toThrow(
209+
errors.ErrorAsyncLocksTimeout,
210+
);
211+
});
212+
await g.next();
213+
await lock.waitForUnlock(100);
214+
});
198215
});

tests/RWLockReader.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,4 +445,21 @@ describe(RWLockReader.name, () => {
445445
expect(lock.readerCount).toBe(0);
446446
expect(lock.writerCount).toBe(0);
447447
});
448+
test('timeout waiting for unlock', async () => {
449+
const lock = new RWLockReader();
450+
await lock.waitForUnlock(100);
451+
await withF([lock.read()], async ([lock]) => {
452+
await expect(lock.waitForUnlock(100)).rejects.toThrow(
453+
errors.ErrorAsyncLocksTimeout,
454+
);
455+
});
456+
await lock.waitForUnlock(100);
457+
const g = withG([lock.write()], async function* ([lock]) {
458+
await expect(lock.waitForUnlock(100)).rejects.toThrow(
459+
errors.ErrorAsyncLocksTimeout,
460+
);
461+
});
462+
await g.next();
463+
await lock.waitForUnlock(100);
464+
});
448465
});

tests/RWLockWriter.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,21 @@ describe(RWLockWriter.name, () => {
442442
expect(lock.readerCount).toBe(0);
443443
expect(lock.writerCount).toBe(0);
444444
});
445+
test('timeout waiting for unlock', async () => {
446+
const lock = new RWLockWriter();
447+
await lock.waitForUnlock(100);
448+
await withF([lock.read()], async ([lock]) => {
449+
await expect(lock.waitForUnlock(100)).rejects.toThrow(
450+
errors.ErrorAsyncLocksTimeout,
451+
);
452+
});
453+
await lock.waitForUnlock(100);
454+
const g = withG([lock.write()], async function* ([lock]) {
455+
await expect(lock.waitForUnlock(100)).rejects.toThrow(
456+
errors.ErrorAsyncLocksTimeout,
457+
);
458+
});
459+
await g.next();
460+
await lock.waitForUnlock(100);
461+
});
445462
});

0 commit comments

Comments
 (0)