@dxos/async 0.3.11-main.fbbdc2a → 0.3.11-main.fc97a54

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.
@@ -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 Lock {
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<() => void>;
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=lock.d.ts.map
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mutex.test.d.ts.map
@@ -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;IAKA,SAAS,CAAC,MAAM,CAAC;IAJxE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0B;IACrD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;gBAEhC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAY,MAAM,CAAC,eAAG;WAU3D,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
+ {"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;IAIX,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAW,OAAO,CAAC,QAAQ,CAAC,SAAS;IAHtE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAA8B;gBAEjB,IAAI,EAAE,OAAO,EAAmB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAE3F,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"}
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.fbbdc2a",
3
+ "version": "0.3.11-main.fc97a54",
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.fbbdc2a",
22
- "@dxos/debug": "0.3.11-main.fbbdc2a",
23
- "@dxos/invariant": "0.3.11-main.fbbdc2a",
24
- "@dxos/util": "0.3.11-main.fbbdc2a",
25
- "@dxos/node-std": "0.3.11-main.fbbdc2a"
21
+ "@dxos/debug": "0.3.11-main.fc97a54",
22
+ "@dxos/invariant": "0.3.11-main.fc97a54",
23
+ "@dxos/context": "0.3.11-main.fc97a54",
24
+ "@dxos/node-std": "0.3.11-main.fc97a54",
25
+ "@dxos/util": "0.3.11-main.fc97a54"
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 './lock';
14
+ export * from './mutex';
15
15
  export * from './sink';
16
16
  export * from './stream-to-array';
17
17
  export * from './timeout';
@@ -7,47 +7,46 @@ import { expect } from 'chai';
7
7
  import { expectToThrow } from '@dxos/debug';
8
8
  import { describe, test } from '@dxos/test';
9
9
 
10
- import { Lock, synchronized } from './lock';
10
+ import { Mutex, synchronized } from './mutex';
11
11
  import { sleep } from './timeout';
12
12
 
13
- describe('Lock', () => {
13
+ describe('Mutex', () => {
14
14
  test('single execution', async () => {
15
15
  const events = [];
16
- const lock = new Lock();
16
+ const mutex = new Mutex();
17
17
 
18
- await lock.executeSynchronized(async () => {
19
- events.push('lock');
18
+ await mutex.executeSynchronized(async () => {
19
+ events.push('mutex');
20
20
  });
21
- events.push('after');
22
21
 
23
- expect(events).to.deep.equal(['lock', 'after']);
22
+ events.push('after');
23
+ expect(events).to.deep.equal(['mutex', 'after']);
24
24
  });
25
25
 
26
26
  test('return value', async () => {
27
- const lock = new Lock();
28
-
29
- const value = await lock.executeSynchronized(async () => 'foo');
27
+ const mutex = new Mutex();
30
28
 
29
+ const value = await mutex.executeSynchronized(async () => 'foo');
31
30
  expect(value).to.equal('foo');
32
31
  });
33
32
 
34
33
  test('two concurrent synchronizations', async () => {
35
34
  const events = [];
36
- const lock = new Lock();
35
+ const mutex = new Mutex();
37
36
 
38
- const p1 = lock
37
+ const p1 = mutex
39
38
  .executeSynchronized(async () => {
40
- events.push('lock1');
39
+ events.push('mutex1');
41
40
  await sleep(10);
42
- events.push('lock2');
41
+ events.push('mutex2');
43
42
  })
44
43
  .then(() => {
45
44
  events.push('p1 resolve');
46
45
  });
47
46
 
48
- const p2 = lock
47
+ const p2 = mutex
49
48
  .executeSynchronized(async () => {
50
- events.push('lock3');
49
+ events.push('mutex3');
51
50
  })
52
51
  .then(() => {
53
52
  events.push('p2 resolve');
@@ -57,14 +56,14 @@ describe('Lock', () => {
57
56
  await p2;
58
57
  events.push('after');
59
58
 
60
- expect(events).to.deep.equal(['lock1', 'lock2', 'p1 resolve', 'lock3', 'p2 resolve', 'after']);
59
+ expect(events).to.deep.equal(['mutex1', 'mutex2', 'p1 resolve', 'mutex3', 'p2 resolve', 'after']);
61
60
  });
62
61
 
63
- test('deadlock', async () => {
64
- const lock = new Lock();
62
+ test('deadmutex', async () => {
63
+ const mutex = new Mutex();
65
64
 
66
- const promise = lock.executeSynchronized(async () => {
67
- await lock.executeSynchronized(async () => {
65
+ const promise = mutex.executeSynchronized(async () => {
66
+ await mutex.executeSynchronized(async () => {
68
67
  /* No-op. */
69
68
  });
70
69
  });
@@ -79,12 +78,12 @@ describe('Lock', () => {
79
78
  expect(resolved).to.be.false;
80
79
  });
81
80
 
82
- test('errors do not break the lock', async () => {
83
- const lock = new Lock();
81
+ test('errors do not break the mutex', async () => {
82
+ const mutex = new Mutex();
84
83
 
85
84
  let p1Status, p2Status;
86
85
 
87
- const p1 = lock
86
+ const p1 = mutex
88
87
  .executeSynchronized(async () => {
89
88
  throw new Error();
90
89
  })
@@ -97,7 +96,7 @@ describe('Lock', () => {
97
96
  },
98
97
  );
99
98
 
100
- const p2 = lock
99
+ const p2 = mutex
101
100
  .executeSynchronized(async () => {
102
101
  /* No-op. */
103
102
  })
@@ -118,16 +117,16 @@ describe('Lock', () => {
118
117
  });
119
118
 
120
119
  test('errors are propagated with stack traces', async () => {
121
- const lock = new Lock();
120
+ const mutex = new Mutex();
122
121
 
123
122
  const throwsError = async () => {
124
123
  throw new Error();
125
124
  };
126
125
 
127
126
  let error: Error;
128
- const callLock = async () => {
127
+ const callmutex = async () => {
129
128
  try {
130
- await lock.executeSynchronized(async () => {
129
+ await mutex.executeSynchronized(async () => {
131
130
  await throwsError();
132
131
  });
133
132
  } catch (err: any) {
@@ -136,11 +135,35 @@ describe('Lock', () => {
136
135
  }
137
136
  };
138
137
 
139
- await expectToThrow(() => callLock());
138
+ await expectToThrow(() => callmutex());
140
139
 
141
140
  expect(error!.stack!.includes('throwsError')).to.be.true;
142
- expect(error!.stack!.includes('callLock')).to.be.true;
141
+ expect(error!.stack!.includes('callmutex')).to.be.true;
143
142
  }).skipEnvironments('webkit');
143
+
144
+ // TODO(burdon): TypeError: Property [Symbol.dispose] is not a function.
145
+ test.skip('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 {
@@ -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 Lock {
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<() => void> {
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 release!: () => void;
42
+ let guard!: MutexGuard;
43
43
  this._queue = new Promise((resolve) => {
44
- release = () => {
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 release;
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 release = await this.acquire();
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
- const classLockSymbol = Symbol('class-lock');
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
- [classLockSymbol]?: Lock;
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 lock: Lock = (this[classLockSymbol] ??= new Lock());
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 release;
118
+ let guard;
104
119
  if (!enableWarning) {
105
- release = await lock.acquire(tag);
120
+ guard = await mutex.acquire(tag);
106
121
  } else {
107
- release = await warnAfterTimeout(10_000, `lock on ${tag} (taken by ${lock.tag})`, () => lock.acquire(tag));
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(subscriber: Observable<T> | Subscriber<T>, protected _value?: T) {
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;
@@ -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(private readonly _ctx: Context, private readonly _callback: () => Promise<void>) {}
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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=lock.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lock.test.d.ts","sourceRoot":"","sources":["../../../src/lock.test.ts"],"names":[],"mappings":""}