@dxos/async 0.7.5-labs.c0e040f → 0.7.5-labs.d199c0f
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 +458 -341
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +440 -326
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +458 -341
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/index.d.ts +6 -5
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/persistent-lifecycle.d.ts +45 -0
- package/dist/types/src/persistent-lifecycle.d.ts.map +1 -0
- package/dist/types/src/persistent-lifecycle.test.d.ts +2 -0
- package/dist/types/src/persistent-lifecycle.test.d.ts.map +1 -0
- package/package.json +8 -7
- package/src/index.ts +6 -5
- package/src/persistent-lifecycle.test.ts +77 -0
- package/src/persistent-lifecycle.ts +128 -0
|
@@ -507,6 +507,98 @@ var latch = ({ count = 1, timeout } = {}) => {
|
|
|
507
507
|
];
|
|
508
508
|
};
|
|
509
509
|
|
|
510
|
+
// packages/common/async/src/mutex.ts
|
|
511
|
+
import "@dxos/util";
|
|
512
|
+
import { warnAfterTimeout } from "@dxos/debug";
|
|
513
|
+
var Mutex = class {
|
|
514
|
+
constructor() {
|
|
515
|
+
this._queue = Promise.resolve();
|
|
516
|
+
this._queueLength = 0;
|
|
517
|
+
this._tag = null;
|
|
518
|
+
}
|
|
519
|
+
get tag() {
|
|
520
|
+
return this._tag;
|
|
521
|
+
}
|
|
522
|
+
isLocked() {
|
|
523
|
+
return this._queueLength > 0;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Acquires the lock.
|
|
527
|
+
* Caller is responsible for releasing the lock using the returned callback.
|
|
528
|
+
* NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
|
|
529
|
+
* @returns Release callback
|
|
530
|
+
*/
|
|
531
|
+
async acquire(tag) {
|
|
532
|
+
const prev = this._queue;
|
|
533
|
+
let guard;
|
|
534
|
+
this._queueLength++;
|
|
535
|
+
this._queue = new Promise((resolve) => {
|
|
536
|
+
guard = new MutexGuard(() => {
|
|
537
|
+
this._queueLength--;
|
|
538
|
+
this._tag = null;
|
|
539
|
+
resolve();
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
await prev;
|
|
543
|
+
if (tag !== void 0) {
|
|
544
|
+
this._tag = tag;
|
|
545
|
+
}
|
|
546
|
+
return guard;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Waits for all previous executions to complete and then executes a given function.
|
|
550
|
+
* Only a single function can be executed at a time.
|
|
551
|
+
* Function are executed in the same order as `executeSynchronized` is called.
|
|
552
|
+
* WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
|
|
553
|
+
*/
|
|
554
|
+
async executeSynchronized(fun) {
|
|
555
|
+
const guard = await this.acquire();
|
|
556
|
+
try {
|
|
557
|
+
return await fun();
|
|
558
|
+
} finally {
|
|
559
|
+
guard.release();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
var MutexGuard = class {
|
|
564
|
+
constructor(_release) {
|
|
565
|
+
this._release = _release;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Releases the lock.
|
|
569
|
+
*/
|
|
570
|
+
release() {
|
|
571
|
+
this._release();
|
|
572
|
+
}
|
|
573
|
+
[Symbol.dispose]() {
|
|
574
|
+
this.release();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
var classMutexSymbol = Symbol("class-mutex");
|
|
578
|
+
var FORCE_DISABLE_WARNING = false;
|
|
579
|
+
var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
|
|
580
|
+
var synchronized = (target, propertyName, descriptor) => {
|
|
581
|
+
const method = descriptor.value;
|
|
582
|
+
descriptor.value = async function synchronizedMethod(...args) {
|
|
583
|
+
const mutex = this[classMutexSymbol] ??= new Mutex();
|
|
584
|
+
const tag = `${target.constructor.name}.${propertyName}`;
|
|
585
|
+
let guard;
|
|
586
|
+
if (!enableWarning) {
|
|
587
|
+
guard = await mutex.acquire(tag);
|
|
588
|
+
} else {
|
|
589
|
+
guard = await warnAfterTimeout(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
|
|
590
|
+
}
|
|
591
|
+
try {
|
|
592
|
+
return await method.apply(this, args);
|
|
593
|
+
} finally {
|
|
594
|
+
guard.release();
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
Object.defineProperty(descriptor.value, "name", {
|
|
598
|
+
value: propertyName + "$synchronized"
|
|
599
|
+
});
|
|
600
|
+
};
|
|
601
|
+
|
|
510
602
|
// packages/common/async/src/observable.ts
|
|
511
603
|
import Observable from "zen-observable";
|
|
512
604
|
import PushStream from "zen-push";
|
|
@@ -752,97 +844,335 @@ var CancellableObservableProvider = class extends ObservableProvider {
|
|
|
752
844
|
}
|
|
753
845
|
};
|
|
754
846
|
|
|
755
|
-
// packages/common/async/src/
|
|
756
|
-
import "@dxos/
|
|
757
|
-
import { warnAfterTimeout } from "@dxos/debug";
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
847
|
+
// packages/common/async/src/persistent-lifecycle.ts
|
|
848
|
+
import { LifecycleState, Resource, cancelWithContext } from "@dxos/context";
|
|
849
|
+
import { warnAfterTimeout as warnAfterTimeout2 } from "@dxos/debug";
|
|
850
|
+
import { log as log2 } from "@dxos/log";
|
|
851
|
+
|
|
852
|
+
// packages/common/async/src/task-scheduling.ts
|
|
853
|
+
import { ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
|
|
854
|
+
import { StackTrace as StackTrace2 } from "@dxos/debug";
|
|
855
|
+
|
|
856
|
+
// packages/common/async/src/track-leaks.ts
|
|
857
|
+
import { StackTrace } from "@dxos/debug";
|
|
858
|
+
import { log } from "@dxos/log";
|
|
859
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
|
|
860
|
+
var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
|
|
861
|
+
var openResources = /* @__PURE__ */ new Set();
|
|
862
|
+
var handleSymbol = Symbol("checkLeaksHandle");
|
|
863
|
+
var trackResource = (resourceProvider) => {
|
|
864
|
+
if (!enabled) {
|
|
865
|
+
return () => {
|
|
866
|
+
};
|
|
763
867
|
}
|
|
764
|
-
|
|
765
|
-
|
|
868
|
+
const resource = resourceProvider();
|
|
869
|
+
openResources.add(resource);
|
|
870
|
+
return () => {
|
|
871
|
+
openResources.delete(resource);
|
|
872
|
+
};
|
|
873
|
+
};
|
|
874
|
+
var trackLeaks = (open, close) => (target) => {
|
|
875
|
+
if (!enabled) {
|
|
876
|
+
return;
|
|
766
877
|
}
|
|
767
|
-
|
|
768
|
-
|
|
878
|
+
const openMethod = target.prototype[open];
|
|
879
|
+
const closeMethod = target.prototype[close];
|
|
880
|
+
if (!openMethod || !closeMethod) {
|
|
881
|
+
throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
|
|
882
|
+
}
|
|
883
|
+
{
|
|
884
|
+
target.prototype[open] = async function(...args) {
|
|
885
|
+
this[handleSymbol] = trackResource(() => ({
|
|
886
|
+
name: target.name,
|
|
887
|
+
openStack: new StackTrace()
|
|
888
|
+
}));
|
|
889
|
+
return openMethod.apply(this, args);
|
|
890
|
+
};
|
|
891
|
+
Object.defineProperty(target.prototype[open], "name", {
|
|
892
|
+
value: open + "$checkLeaks"
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
{
|
|
896
|
+
target.prototype[close] = async function(...args) {
|
|
897
|
+
this[handleSymbol]?.();
|
|
898
|
+
return closeMethod.apply(this, args);
|
|
899
|
+
};
|
|
900
|
+
Object.defineProperty(target.prototype[close], "name", {
|
|
901
|
+
value: close + "$checkLeaks"
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
var dumpLeaks = () => {
|
|
906
|
+
if (!enabled) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
log.info(`Leaked resources ${openResources.size}:`, void 0, {
|
|
910
|
+
F: __dxlog_file3,
|
|
911
|
+
L: 82,
|
|
912
|
+
S: void 0,
|
|
913
|
+
C: (f, a) => f(...a)
|
|
914
|
+
});
|
|
915
|
+
for (const resource of openResources) {
|
|
916
|
+
log.info(`- ${resource.name} at`, void 0, {
|
|
917
|
+
F: __dxlog_file3,
|
|
918
|
+
L: 84,
|
|
919
|
+
S: void 0,
|
|
920
|
+
C: (f, a) => f(...a)
|
|
921
|
+
});
|
|
922
|
+
log.info(resource.openStack.getStack(1), void 0, {
|
|
923
|
+
F: __dxlog_file3,
|
|
924
|
+
L: 85,
|
|
925
|
+
S: void 0,
|
|
926
|
+
C: (f, a) => f(...a)
|
|
927
|
+
});
|
|
928
|
+
log.info("\n", void 0, {
|
|
929
|
+
F: __dxlog_file3,
|
|
930
|
+
L: 86,
|
|
931
|
+
S: void 0,
|
|
932
|
+
C: (f, a) => f(...a)
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
if (enabled) {
|
|
937
|
+
global.dxDumpLeaks = dumpLeaks;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// packages/common/async/src/task-scheduling.ts
|
|
941
|
+
var DeferredTask = class {
|
|
942
|
+
constructor(_ctx, _callback) {
|
|
943
|
+
this._ctx = _ctx;
|
|
944
|
+
this._callback = _callback;
|
|
945
|
+
this._scheduled = false;
|
|
946
|
+
this._currentTask = null;
|
|
947
|
+
this._nextTask = new Trigger();
|
|
948
|
+
}
|
|
949
|
+
get scheduled() {
|
|
950
|
+
return this._scheduled;
|
|
769
951
|
}
|
|
770
952
|
/**
|
|
771
|
-
*
|
|
772
|
-
* Caller is responsible for releasing the lock using the returned callback.
|
|
773
|
-
* NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
|
|
774
|
-
* @returns Release callback
|
|
953
|
+
* Schedule the task to run asynchronously.
|
|
775
954
|
*/
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
this.
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
955
|
+
schedule() {
|
|
956
|
+
if (this._scheduled) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
scheduleTask(this._ctx, async () => {
|
|
960
|
+
await this._currentTask;
|
|
961
|
+
this._scheduled = false;
|
|
962
|
+
const completionTrigger = this._nextTask;
|
|
963
|
+
this._nextTask = new Trigger();
|
|
964
|
+
this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
|
|
965
|
+
completionTrigger.wake();
|
|
785
966
|
});
|
|
786
967
|
});
|
|
787
|
-
|
|
788
|
-
if (tag !== void 0) {
|
|
789
|
-
this._tag = tag;
|
|
790
|
-
}
|
|
791
|
-
return guard;
|
|
968
|
+
this._scheduled = true;
|
|
792
969
|
}
|
|
793
970
|
/**
|
|
794
|
-
*
|
|
795
|
-
* Only a single function can be executed at a time.
|
|
796
|
-
* Function are executed in the same order as `executeSynchronized` is called.
|
|
797
|
-
* WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
|
|
971
|
+
* Schedule the task to run and wait for it to finish.
|
|
798
972
|
*/
|
|
799
|
-
async
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
return await fun();
|
|
803
|
-
} finally {
|
|
804
|
-
guard.release();
|
|
973
|
+
async runBlocking() {
|
|
974
|
+
if (this._ctx.disposed) {
|
|
975
|
+
throw new ContextDisposedError2();
|
|
805
976
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
var MutexGuard = class {
|
|
809
|
-
constructor(_release) {
|
|
810
|
-
this._release = _release;
|
|
977
|
+
this.schedule();
|
|
978
|
+
await this._nextTask.wait();
|
|
811
979
|
}
|
|
812
980
|
/**
|
|
813
|
-
*
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
981
|
+
* Waits for the current task to finish if it is running.
|
|
982
|
+
* Does not schedule a new task.
|
|
983
|
+
*/
|
|
984
|
+
async join() {
|
|
985
|
+
await this._currentTask;
|
|
817
986
|
}
|
|
818
|
-
|
|
819
|
-
|
|
987
|
+
};
|
|
988
|
+
var runInContext = (ctx, fn) => {
|
|
989
|
+
try {
|
|
990
|
+
fn();
|
|
991
|
+
} catch (err) {
|
|
992
|
+
ctx.raise(err);
|
|
820
993
|
}
|
|
821
994
|
};
|
|
822
|
-
var
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
} else {
|
|
834
|
-
guard = await warnAfterTimeout(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
|
|
995
|
+
var runInContextAsync = async (ctx, fn) => {
|
|
996
|
+
try {
|
|
997
|
+
await fn();
|
|
998
|
+
} catch (err) {
|
|
999
|
+
ctx.raise(err);
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
var scheduleMicroTask = (ctx, fn) => {
|
|
1003
|
+
queueMicrotask(async () => {
|
|
1004
|
+
if (ctx.disposed) {
|
|
1005
|
+
return;
|
|
835
1006
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1007
|
+
await runInContextAsync(ctx, fn);
|
|
1008
|
+
});
|
|
1009
|
+
};
|
|
1010
|
+
var scheduleTask = (ctx, fn, afterMs) => {
|
|
1011
|
+
const clearTracking = trackResource(() => ({
|
|
1012
|
+
name: `task (${fn.name || "anonymous"})`,
|
|
1013
|
+
openStack: new StackTrace2()
|
|
1014
|
+
}));
|
|
1015
|
+
const timeout = setTimeout(async () => {
|
|
1016
|
+
clearDispose();
|
|
1017
|
+
await runInContextAsync(ctx, fn);
|
|
1018
|
+
clearTracking();
|
|
1019
|
+
}, afterMs);
|
|
1020
|
+
const clearDispose = ctx.onDispose(() => {
|
|
1021
|
+
clearTracking();
|
|
1022
|
+
clearTimeout(timeout);
|
|
1023
|
+
});
|
|
1024
|
+
};
|
|
1025
|
+
var scheduleTaskInterval = (ctx, task, interval) => {
|
|
1026
|
+
const clearTracking = trackResource(() => ({
|
|
1027
|
+
name: `repeating task (${task.name || "anonymous"})`,
|
|
1028
|
+
openStack: new StackTrace2()
|
|
1029
|
+
}));
|
|
1030
|
+
let timeoutId;
|
|
1031
|
+
const run = async () => {
|
|
1032
|
+
await runInContextAsync(ctx, task);
|
|
1033
|
+
if (ctx.disposed) {
|
|
1034
|
+
return;
|
|
840
1035
|
}
|
|
1036
|
+
timeoutId = setTimeout(run, interval);
|
|
841
1037
|
};
|
|
842
|
-
|
|
843
|
-
|
|
1038
|
+
timeoutId = setTimeout(run, interval);
|
|
1039
|
+
ctx.onDispose(() => {
|
|
1040
|
+
clearTracking();
|
|
1041
|
+
clearTimeout(timeoutId);
|
|
844
1042
|
});
|
|
845
1043
|
};
|
|
1044
|
+
var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
|
|
1045
|
+
const clearTracking = trackResource(() => ({
|
|
1046
|
+
name: `repeating task (${task.name || "anonymous"})`,
|
|
1047
|
+
openStack: new StackTrace2()
|
|
1048
|
+
}));
|
|
1049
|
+
let timeoutId;
|
|
1050
|
+
let interval = initialInterval;
|
|
1051
|
+
const repeat = async () => {
|
|
1052
|
+
await runInContextAsync(ctx, task);
|
|
1053
|
+
if (ctx.disposed) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
interval *= 2;
|
|
1057
|
+
timeoutId = setTimeout(repeat, interval);
|
|
1058
|
+
};
|
|
1059
|
+
timeoutId = setTimeout(repeat, interval);
|
|
1060
|
+
ctx.onDispose(() => {
|
|
1061
|
+
clearTracking();
|
|
1062
|
+
clearTimeout(timeoutId);
|
|
1063
|
+
});
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
// packages/common/async/src/persistent-lifecycle.ts
|
|
1067
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
1068
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1069
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1070
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1071
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1072
|
+
}
|
|
1073
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/common/async/src/persistent-lifecycle.ts";
|
|
1074
|
+
var INIT_RESTART_DELAY = 100;
|
|
1075
|
+
var DEFAULT_MAX_RESTART_DELAY = 5e3;
|
|
1076
|
+
var PersistentLifecycle = class extends Resource {
|
|
1077
|
+
constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
|
|
1078
|
+
super();
|
|
1079
|
+
this._currentState = void 0;
|
|
1080
|
+
this._restartTask = void 0;
|
|
1081
|
+
this._restartAfter = 0;
|
|
1082
|
+
this._start = start;
|
|
1083
|
+
this._stop = stop;
|
|
1084
|
+
this._onRestart = onRestart;
|
|
1085
|
+
this._maxRestartDelay = maxRestartDelay;
|
|
1086
|
+
}
|
|
1087
|
+
get state() {
|
|
1088
|
+
return this._currentState;
|
|
1089
|
+
}
|
|
1090
|
+
async _open() {
|
|
1091
|
+
this._restartTask = new DeferredTask(this._ctx, async () => {
|
|
1092
|
+
try {
|
|
1093
|
+
await this._restart();
|
|
1094
|
+
} catch (err) {
|
|
1095
|
+
log2.warn("Restart failed", {
|
|
1096
|
+
err
|
|
1097
|
+
}, {
|
|
1098
|
+
F: __dxlog_file4,
|
|
1099
|
+
L: 72,
|
|
1100
|
+
S: this,
|
|
1101
|
+
C: (f, a) => f(...a)
|
|
1102
|
+
});
|
|
1103
|
+
this._restartTask?.schedule();
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
this._currentState = await this._start().catch((err) => {
|
|
1107
|
+
log2.warn("Start failed", {
|
|
1108
|
+
err
|
|
1109
|
+
}, {
|
|
1110
|
+
F: __dxlog_file4,
|
|
1111
|
+
L: 77,
|
|
1112
|
+
S: this,
|
|
1113
|
+
C: (f, a) => f(...a)
|
|
1114
|
+
});
|
|
1115
|
+
this._restartTask?.schedule();
|
|
1116
|
+
return void 0;
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
async _close() {
|
|
1120
|
+
await this._restartTask?.join();
|
|
1121
|
+
await this._stopCurrentState();
|
|
1122
|
+
this._restartTask = void 0;
|
|
1123
|
+
}
|
|
1124
|
+
async _restart() {
|
|
1125
|
+
log2(`restarting in ${this._restartAfter}ms`, {
|
|
1126
|
+
state: this._lifecycleState
|
|
1127
|
+
}, {
|
|
1128
|
+
F: __dxlog_file4,
|
|
1129
|
+
L: 90,
|
|
1130
|
+
S: this,
|
|
1131
|
+
C: (f, a) => f(...a)
|
|
1132
|
+
});
|
|
1133
|
+
await this._stopCurrentState();
|
|
1134
|
+
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
await cancelWithContext(this._ctx, sleep(this._restartAfter));
|
|
1138
|
+
this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
|
|
1139
|
+
await warnAfterTimeout2(5e3, "Connection establishment takes too long", async () => {
|
|
1140
|
+
this._currentState = await this._start();
|
|
1141
|
+
});
|
|
1142
|
+
this._restartAfter = 0;
|
|
1143
|
+
await this._onRestart?.();
|
|
1144
|
+
}
|
|
1145
|
+
async _stopCurrentState() {
|
|
1146
|
+
if (this._currentState) {
|
|
1147
|
+
try {
|
|
1148
|
+
await this._stop(this._currentState);
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
log2.catch(err, void 0, {
|
|
1151
|
+
F: __dxlog_file4,
|
|
1152
|
+
L: 112,
|
|
1153
|
+
S: this,
|
|
1154
|
+
C: (f, a) => f(...a)
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
this._currentState = void 0;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Scheduling restart should be done from outside.
|
|
1162
|
+
*/
|
|
1163
|
+
scheduleRestart() {
|
|
1164
|
+
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
this._restartTask.schedule();
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
_ts_decorate([
|
|
1171
|
+
synchronized
|
|
1172
|
+
], PersistentLifecycle.prototype, "_open", null);
|
|
1173
|
+
_ts_decorate([
|
|
1174
|
+
synchronized
|
|
1175
|
+
], PersistentLifecycle.prototype, "scheduleRestart", null);
|
|
846
1176
|
|
|
847
1177
|
// packages/common/async/src/sink.ts
|
|
848
1178
|
var sink = (emitter, event, count = 1) => {
|
|
@@ -900,6 +1230,63 @@ var streamToArray = (stream) => {
|
|
|
900
1230
|
return deferred;
|
|
901
1231
|
};
|
|
902
1232
|
|
|
1233
|
+
// packages/common/async/src/test-stream.ts
|
|
1234
|
+
import { Duplex } from "node:stream";
|
|
1235
|
+
var TestStream = class extends Duplex {
|
|
1236
|
+
constructor() {
|
|
1237
|
+
super(...arguments);
|
|
1238
|
+
this._received = Buffer.alloc(0);
|
|
1239
|
+
this._onWrite = new Event();
|
|
1240
|
+
}
|
|
1241
|
+
static async assertConnectivity(stream1, stream2, { timeout = 200 } = {}) {
|
|
1242
|
+
stream1.push("ping");
|
|
1243
|
+
stream2.push("pong");
|
|
1244
|
+
await Promise.all([
|
|
1245
|
+
stream2.assertReceivedAsync("ping", {
|
|
1246
|
+
timeout
|
|
1247
|
+
}),
|
|
1248
|
+
stream1.assertReceivedAsync("pong", {
|
|
1249
|
+
timeout
|
|
1250
|
+
})
|
|
1251
|
+
]);
|
|
1252
|
+
}
|
|
1253
|
+
_write(chunk, encoding, callback) {
|
|
1254
|
+
this._received = Buffer.concat([
|
|
1255
|
+
this._received,
|
|
1256
|
+
chunk
|
|
1257
|
+
]);
|
|
1258
|
+
this._onWrite.emit();
|
|
1259
|
+
callback();
|
|
1260
|
+
}
|
|
1261
|
+
_read(size) {
|
|
1262
|
+
}
|
|
1263
|
+
assertReceivedAsync(data, { timeout = 200 } = {}) {
|
|
1264
|
+
const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
1265
|
+
return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout);
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
// packages/common/async/src/testing.ts
|
|
1270
|
+
var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
|
|
1271
|
+
const stopTime = timeout ? Date.now() + timeout : 0;
|
|
1272
|
+
const trigger2 = new Trigger();
|
|
1273
|
+
const waiter = async () => {
|
|
1274
|
+
while (!stopTime || Date.now() < stopTime) {
|
|
1275
|
+
try {
|
|
1276
|
+
const value = await condition();
|
|
1277
|
+
if (value) {
|
|
1278
|
+
trigger2.wake(value);
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
} catch (err) {
|
|
1282
|
+
}
|
|
1283
|
+
await sleep(interval);
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
setTimeout(waiter, 0);
|
|
1287
|
+
return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
|
|
1288
|
+
};
|
|
1289
|
+
|
|
903
1290
|
// packages/common/async/src/timer.ts
|
|
904
1291
|
var Timer = class {
|
|
905
1292
|
constructor(_callback) {
|
|
@@ -948,27 +1335,6 @@ var Timer = class {
|
|
|
948
1335
|
}
|
|
949
1336
|
};
|
|
950
1337
|
|
|
951
|
-
// packages/common/async/src/testing.ts
|
|
952
|
-
var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
|
|
953
|
-
const stopTime = timeout ? Date.now() + timeout : 0;
|
|
954
|
-
const trigger2 = new Trigger();
|
|
955
|
-
const waiter = async () => {
|
|
956
|
-
while (!stopTime || Date.now() < stopTime) {
|
|
957
|
-
try {
|
|
958
|
-
const value = await condition();
|
|
959
|
-
if (value) {
|
|
960
|
-
trigger2.wake(value);
|
|
961
|
-
break;
|
|
962
|
-
}
|
|
963
|
-
} catch (err) {
|
|
964
|
-
}
|
|
965
|
-
await sleep(interval);
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
setTimeout(waiter, 0);
|
|
969
|
-
return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
|
|
970
|
-
};
|
|
971
|
-
|
|
972
1338
|
// packages/common/async/src/until.ts
|
|
973
1339
|
var until = (cb, timeout) => {
|
|
974
1340
|
return new Promise((resolve, reject) => {
|
|
@@ -1004,256 +1370,6 @@ var untilError = (cb) => {
|
|
|
1004
1370
|
});
|
|
1005
1371
|
};
|
|
1006
1372
|
|
|
1007
|
-
// packages/common/async/src/task-scheduling.ts
|
|
1008
|
-
import { ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
|
|
1009
|
-
import { StackTrace as StackTrace2 } from "@dxos/debug";
|
|
1010
|
-
|
|
1011
|
-
// packages/common/async/src/track-leaks.ts
|
|
1012
|
-
import { StackTrace } from "@dxos/debug";
|
|
1013
|
-
import { log } from "@dxos/log";
|
|
1014
|
-
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
|
|
1015
|
-
var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
|
|
1016
|
-
var openResources = /* @__PURE__ */ new Set();
|
|
1017
|
-
var handleSymbol = Symbol("checkLeaksHandle");
|
|
1018
|
-
var trackResource = (resourceProvider) => {
|
|
1019
|
-
if (!enabled) {
|
|
1020
|
-
return () => {
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
const resource = resourceProvider();
|
|
1024
|
-
openResources.add(resource);
|
|
1025
|
-
return () => {
|
|
1026
|
-
openResources.delete(resource);
|
|
1027
|
-
};
|
|
1028
|
-
};
|
|
1029
|
-
var trackLeaks = (open, close) => (target) => {
|
|
1030
|
-
if (!enabled) {
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
const openMethod = target.prototype[open];
|
|
1034
|
-
const closeMethod = target.prototype[close];
|
|
1035
|
-
if (!openMethod || !closeMethod) {
|
|
1036
|
-
throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
|
|
1037
|
-
}
|
|
1038
|
-
{
|
|
1039
|
-
target.prototype[open] = async function(...args) {
|
|
1040
|
-
this[handleSymbol] = trackResource(() => ({
|
|
1041
|
-
name: target.name,
|
|
1042
|
-
openStack: new StackTrace()
|
|
1043
|
-
}));
|
|
1044
|
-
return openMethod.apply(this, args);
|
|
1045
|
-
};
|
|
1046
|
-
Object.defineProperty(target.prototype[open], "name", {
|
|
1047
|
-
value: open + "$checkLeaks"
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
{
|
|
1051
|
-
target.prototype[close] = async function(...args) {
|
|
1052
|
-
this[handleSymbol]?.();
|
|
1053
|
-
return closeMethod.apply(this, args);
|
|
1054
|
-
};
|
|
1055
|
-
Object.defineProperty(target.prototype[close], "name", {
|
|
1056
|
-
value: close + "$checkLeaks"
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
};
|
|
1060
|
-
var dumpLeaks = () => {
|
|
1061
|
-
if (!enabled) {
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
log.info(`Leaked resources ${openResources.size}:`, void 0, {
|
|
1065
|
-
F: __dxlog_file3,
|
|
1066
|
-
L: 82,
|
|
1067
|
-
S: void 0,
|
|
1068
|
-
C: (f, a) => f(...a)
|
|
1069
|
-
});
|
|
1070
|
-
for (const resource of openResources) {
|
|
1071
|
-
log.info(`- ${resource.name} at`, void 0, {
|
|
1072
|
-
F: __dxlog_file3,
|
|
1073
|
-
L: 84,
|
|
1074
|
-
S: void 0,
|
|
1075
|
-
C: (f, a) => f(...a)
|
|
1076
|
-
});
|
|
1077
|
-
log.info(resource.openStack.getStack(1), void 0, {
|
|
1078
|
-
F: __dxlog_file3,
|
|
1079
|
-
L: 85,
|
|
1080
|
-
S: void 0,
|
|
1081
|
-
C: (f, a) => f(...a)
|
|
1082
|
-
});
|
|
1083
|
-
log.info("\n", void 0, {
|
|
1084
|
-
F: __dxlog_file3,
|
|
1085
|
-
L: 86,
|
|
1086
|
-
S: void 0,
|
|
1087
|
-
C: (f, a) => f(...a)
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
};
|
|
1091
|
-
if (enabled) {
|
|
1092
|
-
global.dxDumpLeaks = dumpLeaks;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// packages/common/async/src/task-scheduling.ts
|
|
1096
|
-
var DeferredTask = class {
|
|
1097
|
-
constructor(_ctx, _callback) {
|
|
1098
|
-
this._ctx = _ctx;
|
|
1099
|
-
this._callback = _callback;
|
|
1100
|
-
this._scheduled = false;
|
|
1101
|
-
this._currentTask = null;
|
|
1102
|
-
this._nextTask = new Trigger();
|
|
1103
|
-
}
|
|
1104
|
-
get scheduled() {
|
|
1105
|
-
return this._scheduled;
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Schedule the task to run asynchronously.
|
|
1109
|
-
*/
|
|
1110
|
-
schedule() {
|
|
1111
|
-
if (this._scheduled) {
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
scheduleTask(this._ctx, async () => {
|
|
1115
|
-
await this._currentTask;
|
|
1116
|
-
this._scheduled = false;
|
|
1117
|
-
const completionTrigger = this._nextTask;
|
|
1118
|
-
this._nextTask = new Trigger();
|
|
1119
|
-
this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
|
|
1120
|
-
completionTrigger.wake();
|
|
1121
|
-
});
|
|
1122
|
-
});
|
|
1123
|
-
this._scheduled = true;
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Schedule the task to run and wait for it to finish.
|
|
1127
|
-
*/
|
|
1128
|
-
async runBlocking() {
|
|
1129
|
-
if (this._ctx.disposed) {
|
|
1130
|
-
throw new ContextDisposedError2();
|
|
1131
|
-
}
|
|
1132
|
-
this.schedule();
|
|
1133
|
-
await this._nextTask.wait();
|
|
1134
|
-
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Waits for the current task to finish if it is running.
|
|
1137
|
-
* Does not schedule a new task.
|
|
1138
|
-
*/
|
|
1139
|
-
async join() {
|
|
1140
|
-
await this._currentTask;
|
|
1141
|
-
}
|
|
1142
|
-
};
|
|
1143
|
-
var runInContext = (ctx, fn) => {
|
|
1144
|
-
try {
|
|
1145
|
-
fn();
|
|
1146
|
-
} catch (err) {
|
|
1147
|
-
ctx.raise(err);
|
|
1148
|
-
}
|
|
1149
|
-
};
|
|
1150
|
-
var runInContextAsync = async (ctx, fn) => {
|
|
1151
|
-
try {
|
|
1152
|
-
await fn();
|
|
1153
|
-
} catch (err) {
|
|
1154
|
-
ctx.raise(err);
|
|
1155
|
-
}
|
|
1156
|
-
};
|
|
1157
|
-
var scheduleMicroTask = (ctx, fn) => {
|
|
1158
|
-
queueMicrotask(async () => {
|
|
1159
|
-
if (ctx.disposed) {
|
|
1160
|
-
return;
|
|
1161
|
-
}
|
|
1162
|
-
await runInContextAsync(ctx, fn);
|
|
1163
|
-
});
|
|
1164
|
-
};
|
|
1165
|
-
var scheduleTask = (ctx, fn, afterMs) => {
|
|
1166
|
-
const clearTracking = trackResource(() => ({
|
|
1167
|
-
name: `task (${fn.name || "anonymous"})`,
|
|
1168
|
-
openStack: new StackTrace2()
|
|
1169
|
-
}));
|
|
1170
|
-
const timeout = setTimeout(async () => {
|
|
1171
|
-
clearDispose();
|
|
1172
|
-
await runInContextAsync(ctx, fn);
|
|
1173
|
-
clearTracking();
|
|
1174
|
-
}, afterMs);
|
|
1175
|
-
const clearDispose = ctx.onDispose(() => {
|
|
1176
|
-
clearTracking();
|
|
1177
|
-
clearTimeout(timeout);
|
|
1178
|
-
});
|
|
1179
|
-
};
|
|
1180
|
-
var scheduleTaskInterval = (ctx, task, interval) => {
|
|
1181
|
-
const clearTracking = trackResource(() => ({
|
|
1182
|
-
name: `repeating task (${task.name || "anonymous"})`,
|
|
1183
|
-
openStack: new StackTrace2()
|
|
1184
|
-
}));
|
|
1185
|
-
let timeoutId;
|
|
1186
|
-
const run = async () => {
|
|
1187
|
-
await runInContextAsync(ctx, task);
|
|
1188
|
-
if (ctx.disposed) {
|
|
1189
|
-
return;
|
|
1190
|
-
}
|
|
1191
|
-
timeoutId = setTimeout(run, interval);
|
|
1192
|
-
};
|
|
1193
|
-
timeoutId = setTimeout(run, interval);
|
|
1194
|
-
ctx.onDispose(() => {
|
|
1195
|
-
clearTracking();
|
|
1196
|
-
clearTimeout(timeoutId);
|
|
1197
|
-
});
|
|
1198
|
-
};
|
|
1199
|
-
var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
|
|
1200
|
-
const clearTracking = trackResource(() => ({
|
|
1201
|
-
name: `repeating task (${task.name || "anonymous"})`,
|
|
1202
|
-
openStack: new StackTrace2()
|
|
1203
|
-
}));
|
|
1204
|
-
let timeoutId;
|
|
1205
|
-
let interval = initialInterval;
|
|
1206
|
-
const repeat = async () => {
|
|
1207
|
-
await runInContextAsync(ctx, task);
|
|
1208
|
-
if (ctx.disposed) {
|
|
1209
|
-
return;
|
|
1210
|
-
}
|
|
1211
|
-
interval *= 2;
|
|
1212
|
-
timeoutId = setTimeout(repeat, interval);
|
|
1213
|
-
};
|
|
1214
|
-
timeoutId = setTimeout(repeat, interval);
|
|
1215
|
-
ctx.onDispose(() => {
|
|
1216
|
-
clearTracking();
|
|
1217
|
-
clearTimeout(timeoutId);
|
|
1218
|
-
});
|
|
1219
|
-
};
|
|
1220
|
-
|
|
1221
|
-
// packages/common/async/src/test-stream.ts
|
|
1222
|
-
import { Duplex } from "node:stream";
|
|
1223
|
-
var TestStream = class extends Duplex {
|
|
1224
|
-
constructor() {
|
|
1225
|
-
super(...arguments);
|
|
1226
|
-
this._received = Buffer.alloc(0);
|
|
1227
|
-
this._onWrite = new Event();
|
|
1228
|
-
}
|
|
1229
|
-
static async assertConnectivity(stream1, stream2, { timeout = 200 } = {}) {
|
|
1230
|
-
stream1.push("ping");
|
|
1231
|
-
stream2.push("pong");
|
|
1232
|
-
await Promise.all([
|
|
1233
|
-
stream2.assertReceivedAsync("ping", {
|
|
1234
|
-
timeout
|
|
1235
|
-
}),
|
|
1236
|
-
stream1.assertReceivedAsync("pong", {
|
|
1237
|
-
timeout
|
|
1238
|
-
})
|
|
1239
|
-
]);
|
|
1240
|
-
}
|
|
1241
|
-
_write(chunk, encoding, callback) {
|
|
1242
|
-
this._received = Buffer.concat([
|
|
1243
|
-
this._received,
|
|
1244
|
-
chunk
|
|
1245
|
-
]);
|
|
1246
|
-
this._onWrite.emit();
|
|
1247
|
-
callback();
|
|
1248
|
-
}
|
|
1249
|
-
_read(size) {
|
|
1250
|
-
}
|
|
1251
|
-
assertReceivedAsync(data, { timeout = 200 } = {}) {
|
|
1252
|
-
const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
1253
|
-
return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout);
|
|
1254
|
-
}
|
|
1255
|
-
};
|
|
1256
|
-
|
|
1257
1373
|
// packages/common/async/src/update-scheduler.ts
|
|
1258
1374
|
var TIME_PERIOD = 1e3;
|
|
1259
1375
|
var UpdateScheduler = class {
|
|
@@ -1335,6 +1451,7 @@ export {
|
|
|
1335
1451
|
MutexGuard,
|
|
1336
1452
|
Observable,
|
|
1337
1453
|
ObservableProvider,
|
|
1454
|
+
PersistentLifecycle,
|
|
1338
1455
|
PushStream,
|
|
1339
1456
|
TestStream,
|
|
1340
1457
|
TimeoutError,
|