Skip to content

Commit

Permalink
Enabled timeout for waitForUnlock
Browse files Browse the repository at this point in the history
  • Loading branch information
CMCDragonkai committed Apr 1, 2022
1 parent fee516b commit c9a599a
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 12 deletions.
19 changes: 16 additions & 3 deletions src/Lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { MutexInterface } from 'async-mutex';
import type { ResourceAcquire } from '@matrixai/resources';
import { Mutex, withTimeout } from 'async-mutex';
import { withF, withG } from '@matrixai/resources';
import { yieldMicro } from './utils';
import { sleep, yieldMicro } from './utils';
import { ErrorAsyncLocksTimeout } from './errors';

class Lock {
Expand Down Expand Up @@ -43,8 +43,21 @@ class Lock {
return this._lock.isLocked();
}

public async waitForUnlock(): Promise<void> {
return this._lock.waitForUnlock();
public async waitForUnlock(timeout?: number): Promise<void> {
if (timeout != null) {
let timedOut = false;
await Promise.race([
this._lock.waitForUnlock(),
sleep(timeout).then(() => {
timedOut = true;
}),
]);
if (timedOut) {
throw new ErrorAsyncLocksTimeout();
}
} else {
await this._lock.waitForUnlock();
}
}

public async withF<T>(
Expand Down
19 changes: 16 additions & 3 deletions src/RWLockReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { MutexInterface } from 'async-mutex';
import type { ResourceAcquire } from '@matrixai/resources';
import { Mutex, withTimeout } from 'async-mutex';
import { withF, withG } from '@matrixai/resources';
import { yieldMicro } from './utils';
import { sleep, yieldMicro } from './utils';
import { ErrorAsyncLocksTimeout } from './errors';

/**
Expand Down Expand Up @@ -83,8 +83,21 @@ class RWLockReader {
return this.lock.isLocked();
}

public async waitForUnlock(): Promise<void> {
return this.lock.waitForUnlock();
public async waitForUnlock(timeout?: number): Promise<void> {
if (timeout != null) {
let timedOut = false;
await Promise.race([
this.lock.waitForUnlock(),
sleep(timeout).then(() => {
timedOut = true;
}),
]);
if (timedOut) {
throw new ErrorAsyncLocksTimeout();
}
} else {
await this.lock.waitForUnlock();
}
}

public async withReadF<T>(
Expand Down
27 changes: 21 additions & 6 deletions src/RWLockWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,27 @@ class RWLockWriter {
return this.readersLock.isLocked() || this.writersLock.isLocked();
}

public async waitForUnlock(): Promise<void> {
await Promise.all([
this.readersLock.waitForUnlock(),
this.writersLock.waitForUnlock(),
]);
return;
public async waitForUnlock(timeout?: number): Promise<void> {
if (timeout != null) {
let timedOut = false;
await Promise.race([
Promise.all([
this.readersLock.waitForUnlock(),
this.writersLock.waitForUnlock(),
]),
sleep(timeout).then(() => {
timedOut = true;
}),
]);
if (timedOut) {
throw new ErrorAsyncLocksTimeout();
}
} else {
await Promise.all([
this.readersLock.waitForUnlock(),
this.writersLock.waitForUnlock(),
]);
}
}

public async withReadF<T>(
Expand Down
17 changes: 17 additions & 0 deletions tests/Lock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,21 @@ describe(Lock.name, () => {
expect(lock.isLocked()).toBe(false);
expect(lock.count).toBe(0);
});
test('timeout waiting for unlock', async () => {
const lock = new Lock();
await lock.waitForUnlock(100);
await withF([lock.lock()], async ([lock]) => {
await expect(lock.waitForUnlock(100)).rejects.toThrow(
errors.ErrorAsyncLocksTimeout,
);
});
await lock.waitForUnlock(100);
const g = withG([lock.lock()], async function* ([lock]) {
await expect(lock.waitForUnlock(100)).rejects.toThrow(
errors.ErrorAsyncLocksTimeout,
);
});
await g.next();
await lock.waitForUnlock(100);
});
});
17 changes: 17 additions & 0 deletions tests/RWLockReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,4 +445,21 @@ describe(RWLockReader.name, () => {
expect(lock.readerCount).toBe(0);
expect(lock.writerCount).toBe(0);
});
test('timeout waiting for unlock', async () => {
const lock = new RWLockReader();
await lock.waitForUnlock(100);
await withF([lock.read()], async ([lock]) => {
await expect(lock.waitForUnlock(100)).rejects.toThrow(
errors.ErrorAsyncLocksTimeout,
);
});
await lock.waitForUnlock(100);
const g = withG([lock.write()], async function* ([lock]) {
await expect(lock.waitForUnlock(100)).rejects.toThrow(
errors.ErrorAsyncLocksTimeout,
);
});
await g.next();
await lock.waitForUnlock(100);
});
});
17 changes: 17 additions & 0 deletions tests/RWLockWriter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,21 @@ describe(RWLockWriter.name, () => {
expect(lock.readerCount).toBe(0);
expect(lock.writerCount).toBe(0);
});
test('timeout waiting for unlock', async () => {
const lock = new RWLockWriter();
await lock.waitForUnlock(100);
await withF([lock.read()], async ([lock]) => {
await expect(lock.waitForUnlock(100)).rejects.toThrow(
errors.ErrorAsyncLocksTimeout,
);
});
await lock.waitForUnlock(100);
const g = withG([lock.write()], async function* ([lock]) {
await expect(lock.waitForUnlock(100)).rejects.toThrow(
errors.ErrorAsyncLocksTimeout,
);
});
await g.next();
await lock.waitForUnlock(100);
});
});

0 comments on commit c9a599a

Please sign in to comment.