-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
153 lines (131 loc) · 2.77 KB
/
index.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// let debug = require('debug')('rw-resource');
export class NotLockedError extends Error
{
name: string;
constructor(msg: string)
{
super(msg);
this.name = 'NotLockedError';
}
}
export interface unlockCB {
(): void;
}
export default class RWRes
{
public value: any;
readers: number;
writers: number;
waiters: any[];
exclusiveWaiters: any[];
constructor(initialValue?: any)
{
/* The actual value */
this.value = initialValue;
/* Current counts */
this.readers = 0;
this.writers = 0;
/*
Queues of promises resolve callbacks to be resolved when the
resource becomes available.
*/
this.exclusiveWaiters = [];
this.waiters = [];
}
public take(exclusive: boolean): Promise<any>
{
return new Promise((resolve, reject) => {
if (exclusive) {
if (this.readers === 0 && this.writers === 0) {
/*
Nothing else is accessing the resource now, so exclusive
access is available.
*/
this.writers++;
resolve(this.value);
} else {
/* This can't be resolved yet */
this.exclusiveWaiters.push(resolve);
}
} else {
if (this.writers === 0 && this.exclusiveWaiters.length === 0) {
/*
Nothing has exclusive access now, and nothing is waiting
for exclusive access so read access can be given.
*/
this.readers++;
resolve(this.value);
} else {
/* This can't be resolved yet */
this.waiters.push(resolve);
}
}
});
}
public leave(): void
{
if (this.writers) {
this.writers--;
} else if (this.readers) {
this.readers--;
} else {
throw new NotLockedError("Resource released that was not in use");
}
/*
If something is waiting for exclusive access then that should be
granted first.
*/
if (this.writers === 0 && this.readers === 0 &&
this.exclusiveWaiters.length > 0
) {
this.giveExclusive();
return;
}
/*
If nothing has exclusive access, and nothing is waiting for it then
we can give out read access to anything that is waiting.
*/
if (this.writers === 0 && this.exclusiveWaiters.length === 0) {
this.giveReaders();
return;
}
}
public lock(exclusive: boolean): Promise<unlockCB>
{
let lock = this;
return lock.take(exclusive)
.then((value) => {
return(() => {
if (lock) {
/* Only the first caller gets to act on the lock */
let l = lock;
lock = null;
l.leave();
}
});
});
}
giveExclusive(): void
{
let w;
if ((w = this.exclusiveWaiters.shift())) {
this.writers++;
w(this.value);
}
}
giveReaders(): void
{
let w;
while ((w = this.waiters.shift())) {
this.readers++;
w(this.value);
}
}
public check(): number
{
return this.readers +
this.writers +
this.waiters.length +
this.exclusiveWaiters.length;
}
}