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