-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rw_lock.ts
89 lines (85 loc) · 2.65 KB
/
rw_lock.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { RawSemaphore } from "./_raw_semaphore.ts";
/**
* A reader-writer lock implementation that allows multiple concurrent reads but only one write at a time.
* Readers can acquire the lock simultaneously as long as there are no writers holding the lock.
* Writers block all other readers and writers until the write operation completes.
*
* ```ts
* import { AsyncValue } from "@core/asyncutil/async-value";
* import { RwLock } from "@core/asyncutil/rw-lock";
*
* const count = new RwLock(new AsyncValue(0));
*
* // rlock should allow multiple readers at a time
* await Promise.all([...Array(10)].map(() => {
* return count.rlock(async (count) => {
* console.log(await count.get());
* });
* }));
*
* // lock should allow only one writer at a time
* await Promise.all([...Array(10)].map(() => {
* return count.lock(async (count) => {
* const v = await count.get();
* console.log(v);
* count.set(v + 1);
* });
* }));
* ```
*/
export class RwLock<T> {
#read = new RawSemaphore(1);
#write = new RawSemaphore(1);
#value: T;
/**
* Creates a new `RwLock` with the specified initial value.
*
* @param value The initial value of the lock.
*/
constructor(value: T) {
this.#value = value;
}
/**
* Acquires the lock for both reading and writing, and invokes the specified function with the current
* value of the lock. All other readers and writers will be blocked until the function completes.
*
* @param fn The function to invoke.
* @returns A promise that resolves to the return value of the specified function.
*/
async lock<R>(fn: (value: T) => R | PromiseLike<R>): Promise<R> {
await this.#write.acquire();
try {
await this.#read.acquire();
try {
return await fn(this.#value);
} finally {
this.#read.release();
}
} finally {
this.#write.release();
}
}
/**
* Acquires the lock for reading, and invokes the specified function with the current value of the lock.
* Other readers can acquire the lock simultaneously, but any writers will be blocked until the function completes.
*
* @param fn The function to invoke.
* @returns A promise that resolves to the return value of the specified function.
*/
async rlock<R>(fn: (value: T) => R | PromiseLike<R>): Promise<R> {
if (this.#write.locked) {
await this.#write.acquire();
}
try {
// Acquire the read lock without waiting to allow multiple readers to access the lock.
this.#read.acquire();
try {
return await fn(this.#value);
} finally {
this.#read.release();
}
} finally {
this.#write.release();
}
}
}