@dxos/async 0.3.11-main.e19f39d → 0.3.11-main.e28df4f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +30 -15
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +31 -15
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/{lock.d.ts → mutex.d.ts} +12 -3
- package/dist/types/src/mutex.d.ts.map +1 -0
- package/dist/types/src/mutex.test.d.ts +2 -0
- package/dist/types/src/mutex.test.d.ts.map +1 -0
- package/dist/types/src/observable.d.ts.map +1 -1
- package/dist/types/src/task-scheduling.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +1 -1
- package/src/{lock.test.ts → mutex.test.ts} +51 -28
- package/src/{lock.ts → mutex.ts} +30 -15
- package/src/observable.ts +4 -1
- package/src/task-scheduling.ts +4 -1
- package/dist/types/src/lock.d.ts.map +0 -1
- package/dist/types/src/lock.test.d.ts +0 -2
- package/dist/types/src/lock.test.d.ts.map +0 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Java docs reference on synchronized sections:
|
|
15
15
|
* https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
|
|
16
16
|
*/
|
|
17
|
-
export declare class
|
|
17
|
+
export declare class Mutex {
|
|
18
18
|
private _queue;
|
|
19
19
|
private _tag;
|
|
20
20
|
get tag(): string | null;
|
|
@@ -24,7 +24,7 @@ export declare class Lock {
|
|
|
24
24
|
* NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
|
|
25
25
|
* @returns Release callback
|
|
26
26
|
*/
|
|
27
|
-
acquire(tag?: string): Promise<
|
|
27
|
+
acquire(tag?: string): Promise<MutexGuard>;
|
|
28
28
|
/**
|
|
29
29
|
* Waits for all previous executions to complete and then executes a given function.
|
|
30
30
|
* Only a single function can be executed at a time.
|
|
@@ -33,10 +33,19 @@ export declare class Lock {
|
|
|
33
33
|
*/
|
|
34
34
|
executeSynchronized<T>(fun: () => Promise<T>): Promise<T>;
|
|
35
35
|
}
|
|
36
|
+
export declare class MutexGuard {
|
|
37
|
+
private readonly _release;
|
|
38
|
+
constructor(_release: () => void);
|
|
39
|
+
/**
|
|
40
|
+
* Releases the lock.
|
|
41
|
+
*/
|
|
42
|
+
release(): void;
|
|
43
|
+
[Symbol.dispose](): void;
|
|
44
|
+
}
|
|
36
45
|
/**
|
|
37
46
|
* Same as `synchronized` in Java.
|
|
38
47
|
* Uses a lock global to the current class instance.
|
|
39
48
|
* This way every synchronized method on the same instance will share a single lock.
|
|
40
49
|
*/
|
|
41
50
|
export declare const synchronized: (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(...args: any) => any>) => void;
|
|
42
|
-
//# sourceMappingURL=
|
|
51
|
+
//# sourceMappingURL=mutex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../../src/mutex.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,IAAI,CAAuB;IAEnC,IAAI,GAAG,kBAEN;IAED;;;;;OAKG;IACG,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAoBhD;;;;;OAKG;IACG,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAShE;AAED,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM,IAAI;IAEjD;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAGzB;AAaD;;;;GAIG;AACH,eAAO,MAAM,YAAY,WACf,GAAG,gBACG,MAAM,gDAC0B,GAAG,KAAK,GAAG,UAuB1D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex.test.d.ts","sourceRoot":"","sources":["../../../src/mutex.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observable.d.ts","sourceRoot":"","sources":["../../../src/observable.ts"],"names":[],"mappings":"AAIA,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAY,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,UAAU,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,CAAC;AAEnD;;;;GAIG;AAIH,qBAAa,mBAAmB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"observable.d.ts","sourceRoot":"","sources":["../../../src/observable.ts"],"names":[],"mappings":"AAIA,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAY,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,UAAU,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,CAAC;AAEnD;;;;GAIG;AAIH,qBAAa,mBAAmB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IAOrD,SAAS,CAAC,MAAM,CAAC;IANnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0B;IACrD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;gBAG1C,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAC/B,MAAM,CAAC,eAAG;WAWN,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC;WAc5F,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,EAAE;IAInC;;OAEG;IACH,MAAM,CAAC,KAAK;IAIZ;;OAEG;IACH,GAAG,IAAI,CAAC;IAWR;;;;OAIG;IACG,IAAI,CAAC,EAAE,OAAO,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKrD,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC;IAIzD,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC;IAO/D,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,aAAa,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC;IAO3G,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,mBAAmB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC;IAIlF,MAAM,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC;IAIhF;;;;;OAKG;IACH,cAAc,CAAC,CAAC,EACd,OAAO,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,CAAC,EAC/C,GAAG,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GACnC,mBAAmB,CAAC,CAAC,CAAC;IAWzB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,SAAS,CAYf;CACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AAEvC;;;;GAIG;AACH,qBAAa,YAAY;
|
|
1
|
+
{"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AAEvC;;;;GAIG;AACH,qBAAa,YAAY;IAKrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAL5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAA8B;gBAG3B,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAGjD,QAAQ;CAkBT;AAED,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,IAAI,SAMxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,OAAO,MAAM,MAAM,aAAa,IAAI,CAAC,kBAMjF,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAS,OAAO,MAAM,MAAM,aAAa,IAAI,CAAC,SAO3E,CAAC;AAEF,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,aAAa,IAAI,CAAC,YAAY,MAAM,SAgBxF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAS,OAAO,QAAQ,MAAM,QAAQ,IAAI,CAAC,YAAY,MAAM,SAqB7F,CAAC;AAEF,eAAO,MAAM,sCAAsC,QAC5C,OAAO,QACN,MAAM,QAAQ,IAAI,CAAC,mBACR,MAAM,SAwBxB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/async",
|
|
3
|
-
"version": "0.3.11-main.
|
|
3
|
+
"version": "0.3.11-main.e28df4f",
|
|
4
4
|
"description": "Async utilities.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"zen-observable": "^0.10.0",
|
|
20
20
|
"zen-push": "^0.3.1",
|
|
21
|
-
"@dxos/context": "0.3.11-main.
|
|
22
|
-
"@dxos/debug": "0.3.11-main.
|
|
23
|
-
"@dxos/invariant": "0.3.11-main.
|
|
24
|
-
"@dxos/node-std": "0.3.11-main.
|
|
25
|
-
"@dxos/util": "0.3.11-main.
|
|
21
|
+
"@dxos/context": "0.3.11-main.e28df4f",
|
|
22
|
+
"@dxos/debug": "0.3.11-main.e28df4f",
|
|
23
|
+
"@dxos/invariant": "0.3.11-main.e28df4f",
|
|
24
|
+
"@dxos/node-std": "0.3.11-main.e28df4f",
|
|
25
|
+
"@dxos/util": "0.3.11-main.e28df4f"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/zen-observable": "^0.8.3"
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export * from './events';
|
|
|
11
11
|
export * from './latch';
|
|
12
12
|
export * from './observable';
|
|
13
13
|
export * from './observable-value';
|
|
14
|
-
export * from './
|
|
14
|
+
export * from './mutex';
|
|
15
15
|
export * from './sink';
|
|
16
16
|
export * from './stream-to-array';
|
|
17
17
|
export * from './timeout';
|
|
@@ -7,47 +7,47 @@ import { expect } from 'chai';
|
|
|
7
7
|
import { expectToThrow } from '@dxos/debug';
|
|
8
8
|
import { describe, test } from '@dxos/test';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { Mutex, synchronized } from './mutex';
|
|
11
11
|
import { sleep } from './timeout';
|
|
12
12
|
|
|
13
|
-
describe('
|
|
13
|
+
describe('Mutex', () => {
|
|
14
14
|
test('single execution', async () => {
|
|
15
15
|
const events = [];
|
|
16
|
-
const
|
|
16
|
+
const mutex = new Mutex();
|
|
17
17
|
|
|
18
|
-
await
|
|
19
|
-
events.push('
|
|
18
|
+
await mutex.executeSynchronized(async () => {
|
|
19
|
+
events.push('mutex');
|
|
20
20
|
});
|
|
21
21
|
events.push('after');
|
|
22
22
|
|
|
23
|
-
expect(events).to.deep.equal(['
|
|
23
|
+
expect(events).to.deep.equal(['mutex', 'after']);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('return value', async () => {
|
|
27
|
-
const
|
|
27
|
+
const mutex = new Mutex();
|
|
28
28
|
|
|
29
|
-
const value = await
|
|
29
|
+
const value = await mutex.executeSynchronized(async () => 'foo');
|
|
30
30
|
|
|
31
31
|
expect(value).to.equal('foo');
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
test('two concurrent synchronizations', async () => {
|
|
35
35
|
const events = [];
|
|
36
|
-
const
|
|
36
|
+
const mutex = new Mutex();
|
|
37
37
|
|
|
38
|
-
const p1 =
|
|
38
|
+
const p1 = mutex
|
|
39
39
|
.executeSynchronized(async () => {
|
|
40
|
-
events.push('
|
|
40
|
+
events.push('mutex1');
|
|
41
41
|
await sleep(10);
|
|
42
|
-
events.push('
|
|
42
|
+
events.push('mutex2');
|
|
43
43
|
})
|
|
44
44
|
.then(() => {
|
|
45
45
|
events.push('p1 resolve');
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
const p2 =
|
|
48
|
+
const p2 = mutex
|
|
49
49
|
.executeSynchronized(async () => {
|
|
50
|
-
events.push('
|
|
50
|
+
events.push('mutex3');
|
|
51
51
|
})
|
|
52
52
|
.then(() => {
|
|
53
53
|
events.push('p2 resolve');
|
|
@@ -57,14 +57,14 @@ describe('Lock', () => {
|
|
|
57
57
|
await p2;
|
|
58
58
|
events.push('after');
|
|
59
59
|
|
|
60
|
-
expect(events).to.deep.equal(['
|
|
60
|
+
expect(events).to.deep.equal(['mutex1', 'mutex2', 'p1 resolve', 'mutex3', 'p2 resolve', 'after']);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
test('
|
|
64
|
-
const
|
|
63
|
+
test('deadmutex', async () => {
|
|
64
|
+
const mutex = new Mutex();
|
|
65
65
|
|
|
66
|
-
const promise =
|
|
67
|
-
await
|
|
66
|
+
const promise = mutex.executeSynchronized(async () => {
|
|
67
|
+
await mutex.executeSynchronized(async () => {
|
|
68
68
|
/* No-op. */
|
|
69
69
|
});
|
|
70
70
|
});
|
|
@@ -79,12 +79,12 @@ describe('Lock', () => {
|
|
|
79
79
|
expect(resolved).to.be.false;
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
test('errors do not break the
|
|
83
|
-
const
|
|
82
|
+
test('errors do not break the mutex', async () => {
|
|
83
|
+
const mutex = new Mutex();
|
|
84
84
|
|
|
85
85
|
let p1Status, p2Status;
|
|
86
86
|
|
|
87
|
-
const p1 =
|
|
87
|
+
const p1 = mutex
|
|
88
88
|
.executeSynchronized(async () => {
|
|
89
89
|
throw new Error();
|
|
90
90
|
})
|
|
@@ -97,7 +97,7 @@ describe('Lock', () => {
|
|
|
97
97
|
},
|
|
98
98
|
);
|
|
99
99
|
|
|
100
|
-
const p2 =
|
|
100
|
+
const p2 = mutex
|
|
101
101
|
.executeSynchronized(async () => {
|
|
102
102
|
/* No-op. */
|
|
103
103
|
})
|
|
@@ -118,16 +118,16 @@ describe('Lock', () => {
|
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
test('errors are propagated with stack traces', async () => {
|
|
121
|
-
const
|
|
121
|
+
const mutex = new Mutex();
|
|
122
122
|
|
|
123
123
|
const throwsError = async () => {
|
|
124
124
|
throw new Error();
|
|
125
125
|
};
|
|
126
126
|
|
|
127
127
|
let error: Error;
|
|
128
|
-
const
|
|
128
|
+
const callmutex = async () => {
|
|
129
129
|
try {
|
|
130
|
-
await
|
|
130
|
+
await mutex.executeSynchronized(async () => {
|
|
131
131
|
await throwsError();
|
|
132
132
|
});
|
|
133
133
|
} catch (err: any) {
|
|
@@ -136,11 +136,34 @@ describe('Lock', () => {
|
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
await expectToThrow(() =>
|
|
139
|
+
await expectToThrow(() => callmutex());
|
|
140
140
|
|
|
141
141
|
expect(error!.stack!.includes('throwsError')).to.be.true;
|
|
142
|
-
expect(error!.stack!.includes('
|
|
142
|
+
expect(error!.stack!.includes('callmutex')).to.be.true;
|
|
143
143
|
}).skipEnvironments('webkit');
|
|
144
|
+
|
|
145
|
+
test('works with explicit resource management syntax', async () => {
|
|
146
|
+
const mutex = new Mutex();
|
|
147
|
+
|
|
148
|
+
const events: string[] = [];
|
|
149
|
+
|
|
150
|
+
const p1 = (async () => {
|
|
151
|
+
using _guard = await mutex.acquire();
|
|
152
|
+
events.push('acquire one');
|
|
153
|
+
await sleep(10);
|
|
154
|
+
events.push('end one');
|
|
155
|
+
})();
|
|
156
|
+
|
|
157
|
+
const p2 = (async () => {
|
|
158
|
+
using _guard = await mutex.acquire();
|
|
159
|
+
events.push('acquire two');
|
|
160
|
+
await sleep(10);
|
|
161
|
+
events.push('end two');
|
|
162
|
+
})();
|
|
163
|
+
|
|
164
|
+
await Promise.all([p1, p2]);
|
|
165
|
+
expect(events).to.deep.equal(['acquire one', 'end one', 'acquire two', 'end two']);
|
|
166
|
+
});
|
|
144
167
|
});
|
|
145
168
|
|
|
146
169
|
class TestClass {
|
package/src/{lock.ts → mutex.ts}
RENAMED
|
@@ -20,7 +20,7 @@ import { warnAfterTimeout } from '@dxos/debug';
|
|
|
20
20
|
* Java docs reference on synchronized sections:
|
|
21
21
|
* https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
|
|
22
22
|
*/
|
|
23
|
-
export class
|
|
23
|
+
export class Mutex {
|
|
24
24
|
private _queue = Promise.resolve();
|
|
25
25
|
|
|
26
26
|
private _tag: string | null = null;
|
|
@@ -35,16 +35,16 @@ export class Lock {
|
|
|
35
35
|
* NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
|
|
36
36
|
* @returns Release callback
|
|
37
37
|
*/
|
|
38
|
-
async acquire(tag?: string): Promise<
|
|
38
|
+
async acquire(tag?: string): Promise<MutexGuard> {
|
|
39
39
|
const prev = this._queue;
|
|
40
40
|
|
|
41
41
|
// Immediately update the promise before invoking any async actions so that next invocation waits for our task to complete.
|
|
42
|
-
let
|
|
42
|
+
let guard!: MutexGuard;
|
|
43
43
|
this._queue = new Promise((resolve) => {
|
|
44
|
-
|
|
44
|
+
guard = new MutexGuard(() => {
|
|
45
45
|
this._tag = null;
|
|
46
46
|
resolve();
|
|
47
|
-
};
|
|
47
|
+
});
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
await prev;
|
|
@@ -52,7 +52,7 @@ export class Lock {
|
|
|
52
52
|
if (tag !== undefined) {
|
|
53
53
|
this._tag = tag;
|
|
54
54
|
}
|
|
55
|
-
return
|
|
55
|
+
return guard;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -62,20 +62,35 @@ export class Lock {
|
|
|
62
62
|
* WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
|
|
63
63
|
*/
|
|
64
64
|
async executeSynchronized<T>(fun: () => Promise<T>): Promise<T> {
|
|
65
|
-
const
|
|
65
|
+
const guard = await this.acquire();
|
|
66
66
|
|
|
67
67
|
try {
|
|
68
68
|
return await fun();
|
|
69
69
|
} finally {
|
|
70
|
-
release();
|
|
70
|
+
guard.release();
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
export class MutexGuard {
|
|
76
|
+
constructor(private readonly _release: () => void) {}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Releases the lock.
|
|
80
|
+
*/
|
|
81
|
+
release(): void {
|
|
82
|
+
this._release();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
[Symbol.dispose](): void {
|
|
86
|
+
this.release();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const classMutexSymbol = Symbol('class-mutex');
|
|
76
91
|
|
|
77
92
|
interface LockableClass {
|
|
78
|
-
[
|
|
93
|
+
[classMutexSymbol]?: Mutex;
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
const FORCE_DISABLE_WARNING = false;
|
|
@@ -95,22 +110,22 @@ export const synchronized = (
|
|
|
95
110
|
) => {
|
|
96
111
|
const method = descriptor.value!;
|
|
97
112
|
descriptor.value = async function synchronizedMethod(this: any & LockableClass, ...args: any) {
|
|
98
|
-
const
|
|
113
|
+
const mutex: Mutex = (this[classMutexSymbol] ??= new Mutex());
|
|
99
114
|
|
|
100
115
|
const tag = `${target.constructor.name}.${propertyName}`;
|
|
101
116
|
|
|
102
117
|
// Disable warning in prod to avoid performance penalty.
|
|
103
|
-
let
|
|
118
|
+
let guard;
|
|
104
119
|
if (!enableWarning) {
|
|
105
|
-
|
|
120
|
+
guard = await mutex.acquire(tag);
|
|
106
121
|
} else {
|
|
107
|
-
|
|
122
|
+
guard = await warnAfterTimeout(10_000, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
try {
|
|
111
126
|
return await method.apply(this, args);
|
|
112
127
|
} finally {
|
|
113
|
-
release();
|
|
128
|
+
guard.release();
|
|
114
129
|
}
|
|
115
130
|
};
|
|
116
131
|
Object.defineProperty(descriptor.value, 'name', { value: propertyName + '$synchronized' });
|
package/src/observable.ts
CHANGED
|
@@ -24,7 +24,10 @@ export class MulticastObservable<T> extends Observable<T> {
|
|
|
24
24
|
private readonly _observable: Observable<T>;
|
|
25
25
|
private readonly _completed = new Trigger();
|
|
26
26
|
|
|
27
|
-
constructor(
|
|
27
|
+
constructor(
|
|
28
|
+
subscriber: Observable<T> | Subscriber<T>,
|
|
29
|
+
protected _value?: T,
|
|
30
|
+
) {
|
|
28
31
|
super((observer) => this._subscribe(observer));
|
|
29
32
|
|
|
30
33
|
this._observable = typeof subscriber === 'function' ? new Observable(subscriber) : subscriber;
|
package/src/task-scheduling.ts
CHANGED
|
@@ -19,7 +19,10 @@ export class DeferredTask {
|
|
|
19
19
|
private _scheduled = false;
|
|
20
20
|
private _promise: Promise<void> | null = null; // Can't be rejected.
|
|
21
21
|
|
|
22
|
-
constructor(
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly _ctx: Context,
|
|
24
|
+
private readonly _callback: () => Promise<void>,
|
|
25
|
+
) {}
|
|
23
26
|
|
|
24
27
|
schedule() {
|
|
25
28
|
if (this._scheduled) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../src/lock.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,IAAI;IACf,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,IAAI,CAAuB;IAEnC,IAAI,GAAG,kBAEN;IAED;;;;;OAKG;IACG,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAoBhD;;;;;OAKG;IACG,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAShE;AAaD;;;;GAIG;AACH,eAAO,MAAM,YAAY,WACf,GAAG,gBACG,MAAM,gDAC0B,GAAG,KAAK,GAAG,UAuB1D,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock.test.d.ts","sourceRoot":"","sources":["../../../src/lock.test.ts"],"names":[],"mappings":""}
|