@dxos/async 0.5.0 → 0.5.1-main.0ec204c
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 +45 -8
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +45 -8
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/mutex.d.ts +2 -0
- package/dist/types/src/mutex.d.ts.map +1 -1
- package/dist/types/src/task-scheduling.d.ts +14 -1
- package/dist/types/src/task-scheduling.d.ts.map +1 -1
- package/dist/types/src/trigger.d.ts +4 -1
- package/dist/types/src/trigger.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/mutex.ts +8 -2
- package/src/task-scheduling.test.ts +24 -1
- package/src/task-scheduling.ts +33 -4
- package/src/trigger.test.ts +10 -1
- package/src/trigger.ts +10 -5
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/common/async/src/callback.ts":{"bytes":1550,"imports":[],"format":"esm"},"packages/common/async/src/chain.ts":{"bytes":1661,"imports":[],"format":"esm"},"packages/common/async/src/debounce.ts":{"bytes":1235,"imports":[],"format":"esm"},"packages/common/async/src/errors.ts":{"bytes":2956,"imports":[],"format":"esm"},"packages/common/async/src/timeout.ts":{"bytes":7402,"imports":[{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"packages/common/async/src/callback.ts","kind":"import-statement","original":"./callback"},{"path":"packages/common/async/src/errors.ts","kind":"import-statement","original":"./errors"}],"format":"esm"},"packages/common/async/src/event-emitter.ts":{"bytes":4168,"imports":[{"path":"packages/common/async/src/timeout.ts","kind":"import-statement","original":"./timeout"}],"format":"esm"},"packages/common/async/src/events.ts":{"bytes":34729,"imports":[{"path":"@dxos/context","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/latch.ts":{"bytes":4311,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/trigger.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/common/async/src/callback.ts":{"bytes":1550,"imports":[],"format":"esm"},"packages/common/async/src/chain.ts":{"bytes":1661,"imports":[],"format":"esm"},"packages/common/async/src/debounce.ts":{"bytes":1235,"imports":[],"format":"esm"},"packages/common/async/src/errors.ts":{"bytes":2956,"imports":[],"format":"esm"},"packages/common/async/src/timeout.ts":{"bytes":7402,"imports":[{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"packages/common/async/src/callback.ts","kind":"import-statement","original":"./callback"},{"path":"packages/common/async/src/errors.ts","kind":"import-statement","original":"./errors"}],"format":"esm"},"packages/common/async/src/event-emitter.ts":{"bytes":4168,"imports":[{"path":"packages/common/async/src/timeout.ts","kind":"import-statement","original":"./timeout"}],"format":"esm"},"packages/common/async/src/events.ts":{"bytes":34729,"imports":[{"path":"@dxos/context","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/latch.ts":{"bytes":4311,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/trigger.ts":{"bytes":8304,"imports":[{"path":"packages/common/async/src/errors.ts","kind":"import-statement","original":"./errors"},{"path":"packages/common/async/src/timeout.ts","kind":"import-statement","original":"./timeout"}],"format":"esm"},"packages/common/async/src/observable.ts":{"bytes":17867,"imports":[{"path":"zen-observable","kind":"import-statement","external":true},{"path":"zen-push","kind":"import-statement","external":true},{"path":"packages/common/async/src/trigger.ts","kind":"import-statement","original":"./trigger"}],"format":"esm"},"packages/common/async/src/observable-value.ts":{"bytes":6522,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/mutex.ts":{"bytes":12684,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/sink.ts":{"bytes":2003,"imports":[{"path":"packages/common/async/src/trigger.ts","kind":"import-statement","original":"./trigger"}],"format":"esm"},"packages/common/async/src/stream-to-array.ts":{"bytes":4948,"imports":[],"format":"esm"},"packages/common/async/src/timer.ts":{"bytes":5962,"imports":[{"path":"packages/common/async/src/events.ts","kind":"import-statement","original":"./events"}],"format":"esm"},"packages/common/async/src/testing.ts":{"bytes":4680,"imports":[{"path":"packages/common/async/src/timeout.ts","kind":"import-statement","original":"./timeout"},{"path":"packages/common/async/src/trigger.ts","kind":"import-statement","original":"./trigger"}],"format":"esm"},"packages/common/async/src/types.ts":{"bytes":485,"imports":[],"format":"esm"},"packages/common/async/src/until.ts":{"bytes":4324,"imports":[],"format":"esm"},"packages/common/async/src/track-leaks.ts":{"bytes":8435,"imports":[{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true}],"format":"esm"},"packages/common/async/src/task-scheduling.ts":{"bytes":15196,"imports":[{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"packages/common/async/src/track-leaks.ts","kind":"import-statement","original":"./track-leaks"},{"path":"packages/common/async/src/trigger.ts","kind":"import-statement","original":"./trigger"}],"format":"esm"},"packages/common/async/src/test-stream.ts":{"bytes":5011,"imports":[{"path":"node:stream","kind":"import-statement","external":true},{"path":"packages/common/async/src/events.ts","kind":"import-statement","original":"./events"},{"path":"packages/common/async/src/timeout.ts","kind":"import-statement","original":"./timeout"}],"format":"esm"},"packages/common/async/src/update-scheduler.ts":{"bytes":8352,"imports":[{"path":"packages/common/async/src/task-scheduling.ts","kind":"import-statement","original":"./task-scheduling"}],"format":"esm"},"packages/common/async/src/index.ts":{"bytes":2363,"imports":[{"path":"packages/common/async/src/callback.ts","kind":"import-statement","original":"./callback"},{"path":"packages/common/async/src/chain.ts","kind":"import-statement","original":"./chain"},{"path":"packages/common/async/src/debounce.ts","kind":"import-statement","original":"./debounce"},{"path":"packages/common/async/src/errors.ts","kind":"import-statement","original":"./errors"},{"path":"packages/common/async/src/event-emitter.ts","kind":"import-statement","original":"./event-emitter"},{"path":"packages/common/async/src/events.ts","kind":"import-statement","original":"./events"},{"path":"packages/common/async/src/latch.ts","kind":"import-statement","original":"./latch"},{"path":"packages/common/async/src/observable.ts","kind":"import-statement","original":"./observable"},{"path":"packages/common/async/src/observable-value.ts","kind":"import-statement","original":"./observable-value"},{"path":"packages/common/async/src/mutex.ts","kind":"import-statement","original":"./mutex"},{"path":"packages/common/async/src/sink.ts","kind":"import-statement","original":"./sink"},{"path":"packages/common/async/src/stream-to-array.ts","kind":"import-statement","original":"./stream-to-array"},{"path":"packages/common/async/src/timeout.ts","kind":"import-statement","original":"./timeout"},{"path":"packages/common/async/src/timer.ts","kind":"import-statement","original":"./timer"},{"path":"packages/common/async/src/testing.ts","kind":"import-statement","original":"./testing"},{"path":"packages/common/async/src/trigger.ts","kind":"import-statement","original":"./trigger"},{"path":"packages/common/async/src/types.ts","kind":"import-statement","original":"./types"},{"path":"packages/common/async/src/until.ts","kind":"import-statement","original":"./until"},{"path":"packages/common/async/src/task-scheduling.ts","kind":"import-statement","original":"./task-scheduling"},{"path":"packages/common/async/src/test-stream.ts","kind":"import-statement","original":"./test-stream"},{"path":"packages/common/async/src/track-leaks.ts","kind":"import-statement","original":"./track-leaks"},{"path":"packages/common/async/src/update-scheduler.ts","kind":"import-statement","original":"./update-scheduler"}],"format":"esm"}},"outputs":{"packages/common/async/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":81160},"packages/common/async/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"zen-observable","kind":"import-statement","external":true},{"path":"zen-push","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"node:stream","kind":"import-statement","external":true}],"exports":["CancellableObservableProvider","DeferredTask","Event","EventSubscriptions","MulticastObservable","Mutex","MutexGuard","Observable","ObservableProvider","PushStream","TestStream","TimeoutError","Timer","Trigger","UpdateScheduler","addListener","asyncChain","asyncTimeout","createPromiseFromCallback","debounce","dumpLeaks","latch","observableError","onEvent","runInContext","runInContextAsync","scheduleExponentialBackoffTaskInterval","scheduleMicroTask","scheduleTask","scheduleTaskInterval","sink","sleep","sleepWithContext","streamToArray","synchronized","toError","trackLeaks","trackResource","trigger","unrefTimeout","until","untilError","untilPromise","waitForCondition","waitForEvent"],"entryPoint":"packages/common/async/src/index.ts","inputs":{"packages/common/async/src/callback.ts":{"bytesInOutput":191},"packages/common/async/src/index.ts":{"bytesInOutput":0},"packages/common/async/src/chain.ts":{"bytesInOutput":219},"packages/common/async/src/debounce.ts":{"bytesInOutput":144},"packages/common/async/src/errors.ts":{"bytesInOutput":473},"packages/common/async/src/timeout.ts":{"bytesInOutput":1453},"packages/common/async/src/event-emitter.ts":{"bytesInOutput":694},"packages/common/async/src/events.ts":{"bytesInOutput":7419},"packages/common/async/src/latch.ts":{"bytesInOutput":968},"packages/common/async/src/observable.ts":{"bytesInOutput":3695},"packages/common/async/src/trigger.ts":{"bytesInOutput":1446},"packages/common/async/src/observable-value.ts":{"bytesInOutput":975},"packages/common/async/src/mutex.ts":{"bytesInOutput":2444},"packages/common/async/src/sink.ts":{"bytesInOutput":285},"packages/common/async/src/stream-to-array.ts":{"bytesInOutput":967},"packages/common/async/src/timer.ts":{"bytesInOutput":1054},"packages/common/async/src/testing.ts":{"bytesInOutput":587},"packages/common/async/src/until.ts":{"bytesInOutput":760},"packages/common/async/src/task-scheduling.ts":{"bytesInOutput":3090},"packages/common/async/src/track-leaks.ts":{"bytesInOutput":2149},"packages/common/async/src/test-stream.ts":{"bytesInOutput":925},"packages/common/async/src/update-scheduler.ts":{"bytesInOutput":1516}},"bytes":33228}}}
|
|
@@ -17,8 +17,10 @@ import '@dxos/util';
|
|
|
17
17
|
*/
|
|
18
18
|
export declare class Mutex {
|
|
19
19
|
private _queue;
|
|
20
|
+
private _queueLength;
|
|
20
21
|
private _tag;
|
|
21
22
|
get tag(): string | null;
|
|
23
|
+
isLocked(): boolean;
|
|
22
24
|
/**
|
|
23
25
|
* Acquires the lock.
|
|
24
26
|
* Caller is responsible for releasing the lock using the returned callback.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../../src/mutex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../../src/mutex.ts"],"names":[],"mappings":"AAKA,OAAO,YAAY,CAAC;AAGpB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAAK;IAEzB,OAAO,CAAC,IAAI,CAAuB;IAEnC,IAAI,GAAG,kBAEN;IAED,QAAQ,IAAI,OAAO;IAInB;;;;;OAKG;IACG,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAsBhD;;;;;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"}
|
|
@@ -10,9 +10,22 @@ export declare class DeferredTask {
|
|
|
10
10
|
private readonly _ctx;
|
|
11
11
|
private readonly _callback;
|
|
12
12
|
private _scheduled;
|
|
13
|
-
private
|
|
13
|
+
private _currentTask;
|
|
14
|
+
private _nextTask;
|
|
14
15
|
constructor(_ctx: Context, _callback: () => Promise<void>);
|
|
16
|
+
/**
|
|
17
|
+
* Schedule the task to run asynchronously.
|
|
18
|
+
*/
|
|
15
19
|
schedule(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Schedule the task to run and wait for it to finish.
|
|
22
|
+
*/
|
|
23
|
+
runBlocking(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Waits for the current task to finish if it is running.
|
|
26
|
+
* Does not schedule a new task.
|
|
27
|
+
*/
|
|
28
|
+
join(): Promise<void>;
|
|
16
29
|
}
|
|
17
30
|
export declare const runInContext: (ctx: Context, fn: () => void) => void;
|
|
18
31
|
export declare const runInContextAsync: (ctx: Context, fn: () => MaybePromise<void>) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AAEvC;;;;GAIG;AAEH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAiB;gBAGf,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAGjD;;OAEG;IACH,QAAQ;IAuBR;;OAEG;IACG,WAAW;IAQjB;;;OAGG;IACG,IAAI;CAGX;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"}
|
|
@@ -19,7 +19,8 @@ export type TriggerOptions = {
|
|
|
19
19
|
export declare class Trigger<T = void> {
|
|
20
20
|
private _options;
|
|
21
21
|
private _promise;
|
|
22
|
-
private
|
|
22
|
+
private _resolve;
|
|
23
|
+
private _reject;
|
|
23
24
|
constructor(_options?: TriggerOptions);
|
|
24
25
|
/**
|
|
25
26
|
* Wait until wake is called, with optional timeout.
|
|
@@ -29,6 +30,7 @@ export declare class Trigger<T = void> {
|
|
|
29
30
|
}): Promise<T>;
|
|
30
31
|
/**
|
|
31
32
|
* Wake blocked callers (if any).
|
|
33
|
+
* NOOP if the trigger is already resolved.
|
|
32
34
|
*/
|
|
33
35
|
wake(value: T): this;
|
|
34
36
|
/**
|
|
@@ -37,6 +39,7 @@ export declare class Trigger<T = void> {
|
|
|
37
39
|
reset(): this;
|
|
38
40
|
/**
|
|
39
41
|
* Throw error to blocked callers (if any).
|
|
42
|
+
* NOOP if the trigger is already resolved.
|
|
40
43
|
*/
|
|
41
44
|
throw(error: Error): this;
|
|
42
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trigger.d.ts","sourceRoot":"","sources":["../../../src/trigger.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,eAAO,MAAM,OAAO,uBAAwB,MAAM,oCAAkC,IAAI,CAgBvF,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,OAAO,CAAC,CAAC,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"trigger.d.ts","sourceRoot":"","sources":["../../../src/trigger.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,eAAO,MAAM,OAAO,uBAAwB,MAAM,oCAAkC,IAAI,CAgBvF,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,OAAO,CAAC,CAAC,GAAG,IAAI;IAKf,OAAO,CAAC,QAAQ;IAJ5B,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,OAAO,CAA0B;gBAErB,QAAQ,GAAE,cAAqC;IAInE;;OAEG;IACG,IAAI,CAAC,EAAE,OAAO,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAQ9D;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC;IASb;;OAEG;IACH,KAAK;IAUL;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK;CAQnB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/async",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1-main.0ec204c",
|
|
4
4
|
"description": "Async utilities.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"zen-observable": "^0.10.0",
|
|
20
20
|
"zen-push": "^0.3.1",
|
|
21
|
-
"@dxos/
|
|
22
|
-
"@dxos/
|
|
23
|
-
"@dxos/log": "0.5.
|
|
24
|
-
"@dxos/
|
|
25
|
-
"@dxos/node-std": "0.5.
|
|
26
|
-
"@dxos/util": "0.5.
|
|
21
|
+
"@dxos/invariant": "0.5.1-main.0ec204c",
|
|
22
|
+
"@dxos/context": "0.5.1-main.0ec204c",
|
|
23
|
+
"@dxos/log": "0.5.1-main.0ec204c",
|
|
24
|
+
"@dxos/debug": "0.5.1-main.0ec204c",
|
|
25
|
+
"@dxos/node-std": "0.5.1-main.0ec204c",
|
|
26
|
+
"@dxos/util": "0.5.1-main.0ec204c"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/zen-observable": "^0.8.3"
|
package/src/mutex.ts
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { warnAfterTimeout } from '@dxos/debug';
|
|
6
|
-
|
|
7
5
|
// Import explicit resource management polyfill.
|
|
8
6
|
import '@dxos/util';
|
|
7
|
+
import { warnAfterTimeout } from '@dxos/debug';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* A locking mechanism to ensure that a given section of the code is executed by only one single "thread" at a time.
|
|
@@ -25,6 +24,7 @@ import '@dxos/util';
|
|
|
25
24
|
*/
|
|
26
25
|
export class Mutex {
|
|
27
26
|
private _queue = Promise.resolve();
|
|
27
|
+
private _queueLength = 0;
|
|
28
28
|
|
|
29
29
|
private _tag: string | null = null;
|
|
30
30
|
|
|
@@ -32,6 +32,10 @@ export class Mutex {
|
|
|
32
32
|
return this._tag;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
isLocked(): boolean {
|
|
36
|
+
return this._queueLength > 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
/**
|
|
36
40
|
* Acquires the lock.
|
|
37
41
|
* Caller is responsible for releasing the lock using the returned callback.
|
|
@@ -43,8 +47,10 @@ export class Mutex {
|
|
|
43
47
|
|
|
44
48
|
// Immediately update the promise before invoking any async actions so that next invocation waits for our task to complete.
|
|
45
49
|
let guard!: MutexGuard;
|
|
50
|
+
this._queueLength++;
|
|
46
51
|
this._queue = new Promise((resolve) => {
|
|
47
52
|
guard = new MutexGuard(() => {
|
|
53
|
+
this._queueLength--;
|
|
48
54
|
this._tag = null;
|
|
49
55
|
resolve();
|
|
50
56
|
});
|
|
@@ -8,7 +8,7 @@ import waitForExpect from 'wait-for-expect';
|
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { describe, test } from '@dxos/test';
|
|
10
10
|
|
|
11
|
-
import { scheduleTask } from './task-scheduling';
|
|
11
|
+
import { DeferredTask, scheduleTask } from './task-scheduling';
|
|
12
12
|
import { sleep } from './timeout';
|
|
13
13
|
|
|
14
14
|
describe('task-scheduling', () => {
|
|
@@ -43,5 +43,28 @@ describe('task-scheduling', () => {
|
|
|
43
43
|
await sleep(2);
|
|
44
44
|
expect(called).to.be.false;
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
test('run blocking', async () => {
|
|
48
|
+
const events: string[] = [];
|
|
49
|
+
const task = new DeferredTask(Context.default(), async () => {
|
|
50
|
+
await sleep(10);
|
|
51
|
+
events.push('task done');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await task.runBlocking();
|
|
55
|
+
expect(events).to.deep.eq(['task done']);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('join', async () => {
|
|
59
|
+
const task = new DeferredTask(Context.default(), async () => {
|
|
60
|
+
await sleep(10);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await task.join(); // Should be a no-op.
|
|
64
|
+
|
|
65
|
+
await task.runBlocking();
|
|
66
|
+
|
|
67
|
+
await task.join(); // Should be a no-op.
|
|
68
|
+
});
|
|
46
69
|
});
|
|
47
70
|
});
|
package/src/task-scheduling.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Context } from '@dxos/context';
|
|
5
|
+
import { ContextDisposedError, type Context } from '@dxos/context';
|
|
6
6
|
import { StackTrace } from '@dxos/debug';
|
|
7
7
|
import { type MaybePromise } from '@dxos/util';
|
|
8
8
|
|
|
9
9
|
import { trackResource } from './track-leaks';
|
|
10
|
+
import { Trigger } from './trigger';
|
|
10
11
|
|
|
11
12
|
export type ClearCallback = () => void;
|
|
12
13
|
|
|
@@ -15,15 +16,20 @@ export type ClearCallback = () => void;
|
|
|
15
16
|
* Could be triggered multiple times, but only runs once.
|
|
16
17
|
* If a new task is triggered while a previous one is running, the next run would occur immediately after the current run has finished.
|
|
17
18
|
*/
|
|
19
|
+
// TODO(dmaretskyi): Consider calling `join` on context dispose.
|
|
18
20
|
export class DeferredTask {
|
|
19
21
|
private _scheduled = false;
|
|
20
|
-
private
|
|
22
|
+
private _currentTask: Promise<void> | null = null; // Can't be rejected.
|
|
23
|
+
private _nextTask = new Trigger();
|
|
21
24
|
|
|
22
25
|
constructor(
|
|
23
26
|
private readonly _ctx: Context,
|
|
24
27
|
private readonly _callback: () => Promise<void>,
|
|
25
28
|
) {}
|
|
26
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Schedule the task to run asynchronously.
|
|
32
|
+
*/
|
|
27
33
|
schedule() {
|
|
28
34
|
if (this._scheduled) {
|
|
29
35
|
return; // Already scheduled.
|
|
@@ -31,17 +37,40 @@ export class DeferredTask {
|
|
|
31
37
|
|
|
32
38
|
scheduleTask(this._ctx, async () => {
|
|
33
39
|
// The previous task might still be running, so we need to wait for it to finish.
|
|
34
|
-
await this.
|
|
40
|
+
await this._currentTask; // Can't be rejected.
|
|
35
41
|
|
|
36
42
|
// Reset the flag. New tasks can now be scheduled. They would wait for the callback to finish.
|
|
37
43
|
this._scheduled = false;
|
|
44
|
+
const completionTrigger = this._nextTask;
|
|
45
|
+
this._nextTask = new Trigger(); // Re-create the trigger as opposed to resetting it since there might be listeners waiting for it.
|
|
38
46
|
|
|
39
47
|
// Store the promise so that new tasks could wait for this one to finish.
|
|
40
|
-
this.
|
|
48
|
+
this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
|
|
49
|
+
completionTrigger.wake();
|
|
50
|
+
});
|
|
41
51
|
});
|
|
42
52
|
|
|
43
53
|
this._scheduled = true;
|
|
44
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Schedule the task to run and wait for it to finish.
|
|
58
|
+
*/
|
|
59
|
+
async runBlocking() {
|
|
60
|
+
if (this._ctx.disposed) {
|
|
61
|
+
throw new ContextDisposedError();
|
|
62
|
+
}
|
|
63
|
+
this.schedule();
|
|
64
|
+
await this._nextTask.wait();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Waits for the current task to finish if it is running.
|
|
69
|
+
* Does not schedule a new task.
|
|
70
|
+
*/
|
|
71
|
+
async join() {
|
|
72
|
+
await this._currentTask;
|
|
73
|
+
}
|
|
45
74
|
}
|
|
46
75
|
|
|
47
76
|
export const runInContext = (ctx: Context, fn: () => void) => {
|
package/src/trigger.test.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { expect } from 'chai';
|
|
|
6
6
|
|
|
7
7
|
import { describe, test } from '@dxos/test';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { sleep } from './timeout';
|
|
10
|
+
import { Trigger, trigger } from './trigger';
|
|
10
11
|
|
|
11
12
|
describe('trigger', () => {
|
|
12
13
|
test('trigger', async () => {
|
|
@@ -18,4 +19,12 @@ describe('trigger', () => {
|
|
|
18
19
|
expect(result).to.equal('test');
|
|
19
20
|
clearTimeout(t);
|
|
20
21
|
});
|
|
22
|
+
|
|
23
|
+
test('throw with not listeners does not cause unhandled rejection', async () => {
|
|
24
|
+
const trigger = new Trigger();
|
|
25
|
+
trigger.wake();
|
|
26
|
+
trigger.throw(new Error('Test error'));
|
|
27
|
+
trigger.reset();
|
|
28
|
+
await sleep(10);
|
|
29
|
+
});
|
|
21
30
|
});
|
package/src/trigger.ts
CHANGED
|
@@ -43,7 +43,8 @@ export type TriggerOptions = {
|
|
|
43
43
|
*/
|
|
44
44
|
export class Trigger<T = void> {
|
|
45
45
|
private _promise!: Promise<T>;
|
|
46
|
-
private
|
|
46
|
+
private _resolve!: (value: T | PromiseLike<T>) => void;
|
|
47
|
+
private _reject!: (error: Error) => void;
|
|
47
48
|
|
|
48
49
|
constructor(private _options: TriggerOptions = { autoReset: false }) {
|
|
49
50
|
this.reset();
|
|
@@ -62,9 +63,10 @@ export class Trigger<T = void> {
|
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
65
|
* Wake blocked callers (if any).
|
|
66
|
+
* NOOP if the trigger is already resolved.
|
|
65
67
|
*/
|
|
66
68
|
wake(value: T) {
|
|
67
|
-
this.
|
|
69
|
+
this._resolve(value);
|
|
68
70
|
if (this._options.autoReset) {
|
|
69
71
|
return this.reset();
|
|
70
72
|
}
|
|
@@ -76,18 +78,21 @@ export class Trigger<T = void> {
|
|
|
76
78
|
* Reset promise (new waiters will wait).
|
|
77
79
|
*/
|
|
78
80
|
reset() {
|
|
79
|
-
this._promise = new Promise<T>((resolve) => {
|
|
80
|
-
this.
|
|
81
|
+
this._promise = new Promise<T>((resolve, reject) => {
|
|
82
|
+
this._resolve = resolve;
|
|
83
|
+
this._reject = reject;
|
|
81
84
|
});
|
|
85
|
+
this._promise.catch(() => {}); // Prevent unhandled promise rejections.
|
|
82
86
|
|
|
83
87
|
return this;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
/**
|
|
87
91
|
* Throw error to blocked callers (if any).
|
|
92
|
+
* NOOP if the trigger is already resolved.
|
|
88
93
|
*/
|
|
89
94
|
throw(error: Error) {
|
|
90
|
-
this.
|
|
95
|
+
this._reject(error);
|
|
91
96
|
if (this._options.autoReset) {
|
|
92
97
|
return this.reset();
|
|
93
98
|
}
|