1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | Object.defineProperty(exports, "__esModule", { value: true });
|
9 | const list_1 = require("./list");
|
10 | const cancellation_1 = require("./cancellation");
|
11 | const adapter_1 = require("./adapter");
|
12 | const disposable_1 = require("@esfx/disposable");
|
13 |
|
14 |
|
15 |
|
16 | class ReaderWriterLock {
|
17 | constructor() {
|
18 | this._readers = new list_1.LinkedList();
|
19 | this._upgradeables = new list_1.LinkedList();
|
20 | this._upgrades = new list_1.LinkedList();
|
21 | this._writers = new list_1.LinkedList();
|
22 | this._count = 0;
|
23 | }
|
24 | |
25 |
|
26 |
|
27 |
|
28 |
|
29 | read(token) {
|
30 | return new Promise((resolve, reject) => {
|
31 | const _token = adapter_1.getToken(token);
|
32 | _token.throwIfCancellationRequested();
|
33 | if (this._canTakeReadLock()) {
|
34 | resolve(this._takeReadLock());
|
35 | return;
|
36 | }
|
37 | const node = this._readers.push(() => {
|
38 | registration.unregister();
|
39 | if (_token.cancellationRequested) {
|
40 | reject(new cancellation_1.CancelError());
|
41 | }
|
42 | else {
|
43 | resolve(this._takeReadLock());
|
44 | }
|
45 | });
|
46 | const registration = _token.register(() => {
|
47 | if (node.list) {
|
48 | node.list.deleteNode(node);
|
49 | reject(new cancellation_1.CancelError());
|
50 | }
|
51 | });
|
52 | });
|
53 | }
|
54 | |
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | upgradeableRead(token) {
|
61 | return new Promise((resolve, reject) => {
|
62 | const _token = adapter_1.getToken(token);
|
63 | _token.throwIfCancellationRequested();
|
64 | if (this._canTakeUpgradeableReadLock()) {
|
65 | resolve(this._takeUpgradeableReadLock());
|
66 | return;
|
67 | }
|
68 | const node = this._upgradeables.push(() => {
|
69 | registration.unregister();
|
70 | if (_token.cancellationRequested) {
|
71 | reject(new cancellation_1.CancelError());
|
72 | }
|
73 | else {
|
74 | resolve(this._takeUpgradeableReadLock());
|
75 | }
|
76 | });
|
77 | const registration = _token.register(() => {
|
78 | if (node.list) {
|
79 | node.list.deleteNode(node);
|
80 | reject(new cancellation_1.CancelError());
|
81 | }
|
82 | });
|
83 | });
|
84 | }
|
85 | |
86 |
|
87 |
|
88 |
|
89 |
|
90 | write(token) {
|
91 | return new Promise((resolve, reject) => {
|
92 | const _token = adapter_1.getToken(token);
|
93 | _token.throwIfCancellationRequested();
|
94 | if (this._canTakeWriteLock()) {
|
95 | resolve(this._takeWriteLock());
|
96 | return;
|
97 | }
|
98 | const node = this._writers.push(() => {
|
99 | registration.unregister();
|
100 | if (_token.cancellationRequested) {
|
101 | reject(new cancellation_1.CancelError());
|
102 | }
|
103 | else {
|
104 | resolve(this._takeWriteLock());
|
105 | }
|
106 | });
|
107 | const registration = _token.register(() => {
|
108 | if (node.list) {
|
109 | node.list.deleteNode(node);
|
110 | reject(new cancellation_1.CancelError());
|
111 | }
|
112 | });
|
113 | });
|
114 | }
|
115 | _upgrade(token) {
|
116 | return new Promise((resolve, reject) => {
|
117 | const _token = adapter_1.getToken(token);
|
118 | _token.throwIfCancellationRequested();
|
119 | if (this._canTakeUpgradeLock()) {
|
120 | resolve(this._takeUpgradeLock());
|
121 | return;
|
122 | }
|
123 | const node = this._upgrades.push(() => {
|
124 | registration.unregister();
|
125 | if (_token.cancellationRequested) {
|
126 | reject(new cancellation_1.CancelError());
|
127 | }
|
128 | else {
|
129 | resolve(this._takeUpgradeLock());
|
130 | }
|
131 | });
|
132 | const registration = _token.register(() => {
|
133 | if (node.list) {
|
134 | node.list.deleteNode(node);
|
135 | reject(new cancellation_1.CancelError());
|
136 | }
|
137 | });
|
138 | });
|
139 | }
|
140 | _processLockRequests() {
|
141 | if (this._processWriteLockRequest())
|
142 | return;
|
143 | if (this._processUpgradeRequest())
|
144 | return;
|
145 | this._processUpgradeableReadLockRequest();
|
146 | this._processReadLockRequests();
|
147 | }
|
148 | _canTakeReadLock() {
|
149 | return this._count >= 0
|
150 | && this._writers.size === 0
|
151 | && this._upgrades.size === 0
|
152 | && this._writers.size === 0;
|
153 | }
|
154 | _processReadLockRequests() {
|
155 | if (this._canTakeReadLock()) {
|
156 | this._readers.forEach(resolve => resolve());
|
157 | this._readers.clear();
|
158 | }
|
159 | }
|
160 | _takeReadLock() {
|
161 | let released = false;
|
162 | this._count++;
|
163 | const release = () => {
|
164 | if (released)
|
165 | throw new Error("Lock already released.");
|
166 | released = true;
|
167 | this._releaseReadLock();
|
168 | };
|
169 | return {
|
170 | release,
|
171 | [disposable_1.Disposable.dispose]: release,
|
172 | };
|
173 | }
|
174 | _releaseReadLock() {
|
175 | this._count--;
|
176 | this._processLockRequests();
|
177 | }
|
178 | _canTakeUpgradeableReadLock() {
|
179 | return this._count >= 0 && !this._upgradeable;
|
180 | }
|
181 | _processUpgradeableReadLockRequest() {
|
182 | if (this._canTakeUpgradeableReadLock()) {
|
183 | const resolve = this._upgradeables.shift();
|
184 | if (resolve) {
|
185 | resolve();
|
186 | }
|
187 | }
|
188 | }
|
189 | _takeUpgradeableReadLock() {
|
190 | const upgrade = (token) => {
|
191 | if (this._upgradeable !== hold)
|
192 | throw new Error("Lock already released.");
|
193 | return this._upgrade(token);
|
194 | };
|
195 | const release = () => {
|
196 | if (this._upgradeable !== hold)
|
197 | throw new Error("Lock already released.");
|
198 | this._releaseUpgradeableReadLock();
|
199 | };
|
200 | const hold = {
|
201 | upgrade,
|
202 | release,
|
203 | [disposable_1.Disposable.dispose]: release,
|
204 | };
|
205 | this._count++;
|
206 | this._upgradeable = hold;
|
207 | return hold;
|
208 | }
|
209 | _releaseUpgradeableReadLock() {
|
210 | if (this._count === -1) {
|
211 | this._count = 0;
|
212 | }
|
213 | else {
|
214 | this._count--;
|
215 | }
|
216 | this._upgraded = undefined;
|
217 | this._upgradeable = undefined;
|
218 | this._processLockRequests();
|
219 | }
|
220 | _canTakeUpgradeLock() {
|
221 | return this._count === 1
|
222 | && this._upgradeable
|
223 | && !this._upgraded;
|
224 | }
|
225 | _processUpgradeRequest() {
|
226 | if (this._canTakeUpgradeLock()) {
|
227 | const resolve = this._upgrades.shift();
|
228 | if (resolve) {
|
229 | resolve();
|
230 | return true;
|
231 | }
|
232 | }
|
233 | return false;
|
234 | }
|
235 | _takeUpgradeLock() {
|
236 | const release = () => {
|
237 | if (this._upgraded !== hold)
|
238 | throw new Error("Lock already released.");
|
239 | this._releaseUpgradeLock();
|
240 | };
|
241 | const hold = {
|
242 | release,
|
243 | [disposable_1.Disposable.dispose]: release
|
244 | };
|
245 | this._upgraded = hold;
|
246 | this._count = -1;
|
247 | return hold;
|
248 | }
|
249 | _releaseUpgradeLock() {
|
250 | this._upgraded = undefined;
|
251 | this._count = 1;
|
252 | this._processLockRequests();
|
253 | }
|
254 | _canTakeWriteLock() {
|
255 | return this._count === 0;
|
256 | }
|
257 | _processWriteLockRequest() {
|
258 | if (this._canTakeWriteLock()) {
|
259 | const resolve = this._writers.shift();
|
260 | if (resolve) {
|
261 | resolve();
|
262 | return true;
|
263 | }
|
264 | }
|
265 | return false;
|
266 | }
|
267 | _takeWriteLock() {
|
268 | let released = false;
|
269 | this._count = -1;
|
270 | const release = () => {
|
271 | if (released)
|
272 | throw new Error("Lock already released.");
|
273 | released = true;
|
274 | this._releaseWriteLock();
|
275 | };
|
276 | return {
|
277 | release,
|
278 | [disposable_1.Disposable.dispose]: release,
|
279 | };
|
280 | }
|
281 | _releaseWriteLock() {
|
282 | this._count = 0;
|
283 | this._processLockRequests();
|
284 | }
|
285 | }
|
286 | exports.ReaderWriterLock = ReaderWriterLock;
|