@dxos/async 0.7.5-main.9d26e3a → 0.7.5-main.b19bfc8
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 +481 -330
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +475 -327
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +481 -330
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/events.d.ts +13 -2
- package/dist/types/src/events.d.ts.map +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/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/events.test.ts +15 -0
- package/src/events.ts +49 -9
- 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
|
@@ -139,6 +139,7 @@ var EventSubscriptions = class {
|
|
|
139
139
|
this._listeners.length = 0;
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
|
+
var DO_NOT_ERROR_ON_ASYNC_CALLBACK = true;
|
|
142
143
|
var Event = class _Event {
|
|
143
144
|
constructor() {
|
|
144
145
|
this._listeners = /* @__PURE__ */ new Set();
|
|
@@ -167,7 +168,23 @@ var Event = class _Event {
|
|
|
167
168
|
*/
|
|
168
169
|
emit(data) {
|
|
169
170
|
for (const listener of this._listeners) {
|
|
170
|
-
|
|
171
|
+
listener.trigger(data);
|
|
172
|
+
if (listener.once) {
|
|
173
|
+
this._listeners.delete(listener);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Emit an event and wait for async listeners to complete.
|
|
179
|
+
* In most cases should only be called by the class or entity containing the event.
|
|
180
|
+
* All listeners are called in order of subscription with persistent ones first.
|
|
181
|
+
* Listeners are called sequentially.
|
|
182
|
+
*
|
|
183
|
+
* @param data param that will be passed to all listeners.
|
|
184
|
+
*/
|
|
185
|
+
async emitAsync(data) {
|
|
186
|
+
for (const listener of this._listeners) {
|
|
187
|
+
await listener.triggerAsync(data);
|
|
171
188
|
if (listener.once) {
|
|
172
189
|
this._listeners.delete(listener);
|
|
173
190
|
}
|
|
@@ -180,7 +197,7 @@ var Event = class _Event {
|
|
|
180
197
|
] : [
|
|
181
198
|
new Context(void 0, {
|
|
182
199
|
F: __dxlog_file,
|
|
183
|
-
L:
|
|
200
|
+
L: 148
|
|
184
201
|
}),
|
|
185
202
|
_ctx
|
|
186
203
|
];
|
|
@@ -213,7 +230,7 @@ var Event = class _Event {
|
|
|
213
230
|
] : [
|
|
214
231
|
new Context(void 0, {
|
|
215
232
|
F: __dxlog_file,
|
|
216
|
-
L:
|
|
233
|
+
L: 185
|
|
217
234
|
}),
|
|
218
235
|
_ctx
|
|
219
236
|
];
|
|
@@ -397,7 +414,21 @@ var EventListener = class {
|
|
|
397
414
|
derefCallback() {
|
|
398
415
|
return this.weak ? this.callback.deref() : this.callback;
|
|
399
416
|
}
|
|
400
|
-
|
|
417
|
+
trigger(data) {
|
|
418
|
+
let result;
|
|
419
|
+
try {
|
|
420
|
+
const callback = this.derefCallback();
|
|
421
|
+
result = callback?.(data);
|
|
422
|
+
} catch (err) {
|
|
423
|
+
this.ctx.raise(err);
|
|
424
|
+
}
|
|
425
|
+
if (!DO_NOT_ERROR_ON_ASYNC_CALLBACK) {
|
|
426
|
+
if (result instanceof Promise) {
|
|
427
|
+
throw new TypeError("Event has async callbacks, use emitAsync instead");
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async triggerAsync(data) {
|
|
401
432
|
try {
|
|
402
433
|
const callback = this.derefCallback();
|
|
403
434
|
await callback?.(data);
|
|
@@ -476,6 +507,98 @@ var latch = ({ count = 1, timeout } = {}) => {
|
|
|
476
507
|
];
|
|
477
508
|
};
|
|
478
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
|
+
|
|
479
602
|
// packages/common/async/src/observable.ts
|
|
480
603
|
import Observable from "zen-observable";
|
|
481
604
|
import PushStream from "zen-push";
|
|
@@ -721,340 +844,93 @@ var CancellableObservableProvider = class extends ObservableProvider {
|
|
|
721
844
|
}
|
|
722
845
|
};
|
|
723
846
|
|
|
724
|
-
// packages/common/async/src/
|
|
725
|
-
import "@dxos/
|
|
726
|
-
import { warnAfterTimeout } from "@dxos/debug";
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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
|
+
};
|
|
732
867
|
}
|
|
733
|
-
|
|
734
|
-
|
|
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;
|
|
735
877
|
}
|
|
736
|
-
|
|
737
|
-
|
|
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}`);
|
|
738
882
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
this._queue = new Promise((resolve) => {
|
|
750
|
-
guard = new MutexGuard(() => {
|
|
751
|
-
this._queueLength--;
|
|
752
|
-
this._tag = null;
|
|
753
|
-
resolve();
|
|
754
|
-
});
|
|
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"
|
|
755
893
|
});
|
|
756
|
-
await prev;
|
|
757
|
-
if (tag !== void 0) {
|
|
758
|
-
this._tag = tag;
|
|
759
|
-
}
|
|
760
|
-
return guard;
|
|
761
894
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
try {
|
|
771
|
-
return await fun();
|
|
772
|
-
} finally {
|
|
773
|
-
guard.release();
|
|
774
|
-
}
|
|
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
|
+
});
|
|
775
903
|
}
|
|
776
904
|
};
|
|
777
|
-
var
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* Releases the lock.
|
|
783
|
-
*/
|
|
784
|
-
release() {
|
|
785
|
-
this._release();
|
|
905
|
+
var dumpLeaks = () => {
|
|
906
|
+
if (!enabled) {
|
|
907
|
+
return;
|
|
786
908
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
value: propertyName + "$synchronized"
|
|
813
|
-
});
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
// packages/common/async/src/sink.ts
|
|
817
|
-
var sink = (emitter, event, count = 1) => {
|
|
818
|
-
const [getPromise, resolve] = trigger();
|
|
819
|
-
let counter = 0;
|
|
820
|
-
const listener = () => {
|
|
821
|
-
if (++counter === count) {
|
|
822
|
-
emitter.off(event, listener);
|
|
823
|
-
resolve();
|
|
824
|
-
}
|
|
825
|
-
};
|
|
826
|
-
emitter.on(event, listener);
|
|
827
|
-
return getPromise();
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
// packages/common/async/src/stream-to-array.ts
|
|
831
|
-
var streamToArray = (stream) => {
|
|
832
|
-
let deferred;
|
|
833
|
-
if (!stream.readable) {
|
|
834
|
-
deferred = Promise.resolve([]);
|
|
835
|
-
} else {
|
|
836
|
-
deferred = new Promise((resolve, reject) => {
|
|
837
|
-
if (!stream.readable) {
|
|
838
|
-
return resolve([]);
|
|
839
|
-
}
|
|
840
|
-
let arr = [];
|
|
841
|
-
const onData = (doc) => {
|
|
842
|
-
arr?.push(doc);
|
|
843
|
-
};
|
|
844
|
-
const onEnd = (err) => {
|
|
845
|
-
if (err) {
|
|
846
|
-
reject(err);
|
|
847
|
-
} else {
|
|
848
|
-
resolve(arr);
|
|
849
|
-
}
|
|
850
|
-
cleanup();
|
|
851
|
-
};
|
|
852
|
-
const onClose = () => {
|
|
853
|
-
resolve(arr);
|
|
854
|
-
cleanup();
|
|
855
|
-
};
|
|
856
|
-
const cleanup = () => {
|
|
857
|
-
arr = [];
|
|
858
|
-
stream.removeListener("data", onData);
|
|
859
|
-
stream.removeListener("end", onEnd);
|
|
860
|
-
stream.removeListener("error", onEnd);
|
|
861
|
-
stream.removeListener("close", onClose);
|
|
862
|
-
};
|
|
863
|
-
stream.on("data", onData);
|
|
864
|
-
stream.on("end", onEnd);
|
|
865
|
-
stream.on("error", onEnd);
|
|
866
|
-
stream.on("close", onClose);
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
return deferred;
|
|
870
|
-
};
|
|
871
|
-
|
|
872
|
-
// packages/common/async/src/timer.ts
|
|
873
|
-
var Timer = class {
|
|
874
|
-
constructor(_callback) {
|
|
875
|
-
this._callback = _callback;
|
|
876
|
-
this._state = new Event();
|
|
877
|
-
this._count = 0;
|
|
878
|
-
}
|
|
879
|
-
get state() {
|
|
880
|
-
return this._state;
|
|
881
|
-
}
|
|
882
|
-
get running() {
|
|
883
|
-
return !!this._timer;
|
|
884
|
-
}
|
|
885
|
-
start(options, cb) {
|
|
886
|
-
if (isNaN(options.count) || isNaN(options.interval)) {
|
|
887
|
-
throw new Error(`Invalid options: ${JSON.stringify(options)}`);
|
|
888
|
-
}
|
|
889
|
-
if (this.running) {
|
|
890
|
-
this.stop();
|
|
891
|
-
}
|
|
892
|
-
const stop = () => {
|
|
893
|
-
this.stop();
|
|
894
|
-
cb?.();
|
|
895
|
-
};
|
|
896
|
-
const run = () => {
|
|
897
|
-
if (this._count >= (options.count ?? 0)) {
|
|
898
|
-
stop();
|
|
899
|
-
} else {
|
|
900
|
-
const interval = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);
|
|
901
|
-
this._timer = setTimeout(async () => {
|
|
902
|
-
await this._callback(this._count++);
|
|
903
|
-
run();
|
|
904
|
-
}, interval);
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
this._state.emit(true);
|
|
908
|
-
this._count = 0;
|
|
909
|
-
setTimeout(run);
|
|
910
|
-
return this;
|
|
911
|
-
}
|
|
912
|
-
stop() {
|
|
913
|
-
clearInterval(this._timer);
|
|
914
|
-
this._timer = void 0;
|
|
915
|
-
this._state.emit(false);
|
|
916
|
-
return this;
|
|
917
|
-
}
|
|
918
|
-
};
|
|
919
|
-
|
|
920
|
-
// packages/common/async/src/testing.ts
|
|
921
|
-
var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
|
|
922
|
-
const stopTime = timeout ? Date.now() + timeout : 0;
|
|
923
|
-
const trigger2 = new Trigger();
|
|
924
|
-
const waiter = async () => {
|
|
925
|
-
while (!stopTime || Date.now() < stopTime) {
|
|
926
|
-
try {
|
|
927
|
-
const value = await condition();
|
|
928
|
-
if (value) {
|
|
929
|
-
trigger2.wake(value);
|
|
930
|
-
break;
|
|
931
|
-
}
|
|
932
|
-
} catch (err) {
|
|
933
|
-
}
|
|
934
|
-
await sleep(interval);
|
|
935
|
-
}
|
|
936
|
-
};
|
|
937
|
-
setTimeout(waiter, 0);
|
|
938
|
-
return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
|
|
939
|
-
};
|
|
940
|
-
|
|
941
|
-
// packages/common/async/src/until.ts
|
|
942
|
-
var until = (cb, timeout) => {
|
|
943
|
-
return new Promise((resolve, reject) => {
|
|
944
|
-
const t = timeout && setTimeout(() => {
|
|
945
|
-
reject(new Error(`Timeout after ${t}ms`));
|
|
946
|
-
}, timeout);
|
|
947
|
-
setTimeout(async () => {
|
|
948
|
-
try {
|
|
949
|
-
await cb((value) => {
|
|
950
|
-
t && clearTimeout(t);
|
|
951
|
-
resolve(value);
|
|
952
|
-
}, (error) => {
|
|
953
|
-
t && clearTimeout(t);
|
|
954
|
-
reject(error);
|
|
955
|
-
});
|
|
956
|
-
} catch (err) {
|
|
957
|
-
reject(err);
|
|
958
|
-
}
|
|
959
|
-
});
|
|
960
|
-
});
|
|
961
|
-
};
|
|
962
|
-
var untilPromise = (cb) => cb();
|
|
963
|
-
var untilError = (cb) => {
|
|
964
|
-
return new Promise((resolve, reject) => {
|
|
965
|
-
setTimeout(async () => {
|
|
966
|
-
try {
|
|
967
|
-
await cb();
|
|
968
|
-
reject(new Error("No error was thrown."));
|
|
969
|
-
} catch (err) {
|
|
970
|
-
resolve(err);
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
|
-
});
|
|
974
|
-
};
|
|
975
|
-
|
|
976
|
-
// packages/common/async/src/task-scheduling.ts
|
|
977
|
-
import { ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
|
|
978
|
-
import { StackTrace as StackTrace2 } from "@dxos/debug";
|
|
979
|
-
|
|
980
|
-
// packages/common/async/src/track-leaks.ts
|
|
981
|
-
import { StackTrace } from "@dxos/debug";
|
|
982
|
-
import { log } from "@dxos/log";
|
|
983
|
-
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
|
|
984
|
-
var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
|
|
985
|
-
var openResources = /* @__PURE__ */ new Set();
|
|
986
|
-
var handleSymbol = Symbol("checkLeaksHandle");
|
|
987
|
-
var trackResource = (resourceProvider) => {
|
|
988
|
-
if (!enabled) {
|
|
989
|
-
return () => {
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
const resource = resourceProvider();
|
|
993
|
-
openResources.add(resource);
|
|
994
|
-
return () => {
|
|
995
|
-
openResources.delete(resource);
|
|
996
|
-
};
|
|
997
|
-
};
|
|
998
|
-
var trackLeaks = (open, close) => (target) => {
|
|
999
|
-
if (!enabled) {
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
const openMethod = target.prototype[open];
|
|
1003
|
-
const closeMethod = target.prototype[close];
|
|
1004
|
-
if (!openMethod || !closeMethod) {
|
|
1005
|
-
throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
|
|
1006
|
-
}
|
|
1007
|
-
{
|
|
1008
|
-
target.prototype[open] = async function(...args) {
|
|
1009
|
-
this[handleSymbol] = trackResource(() => ({
|
|
1010
|
-
name: target.name,
|
|
1011
|
-
openStack: new StackTrace()
|
|
1012
|
-
}));
|
|
1013
|
-
return openMethod.apply(this, args);
|
|
1014
|
-
};
|
|
1015
|
-
Object.defineProperty(target.prototype[open], "name", {
|
|
1016
|
-
value: open + "$checkLeaks"
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
{
|
|
1020
|
-
target.prototype[close] = async function(...args) {
|
|
1021
|
-
this[handleSymbol]?.();
|
|
1022
|
-
return closeMethod.apply(this, args);
|
|
1023
|
-
};
|
|
1024
|
-
Object.defineProperty(target.prototype[close], "name", {
|
|
1025
|
-
value: close + "$checkLeaks"
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
};
|
|
1029
|
-
var dumpLeaks = () => {
|
|
1030
|
-
if (!enabled) {
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
log.info(`Leaked resources ${openResources.size}:`, void 0, {
|
|
1034
|
-
F: __dxlog_file3,
|
|
1035
|
-
L: 82,
|
|
1036
|
-
S: void 0,
|
|
1037
|
-
C: (f, a) => f(...a)
|
|
1038
|
-
});
|
|
1039
|
-
for (const resource of openResources) {
|
|
1040
|
-
log.info(`- ${resource.name} at`, void 0, {
|
|
1041
|
-
F: __dxlog_file3,
|
|
1042
|
-
L: 84,
|
|
1043
|
-
S: void 0,
|
|
1044
|
-
C: (f, a) => f(...a)
|
|
1045
|
-
});
|
|
1046
|
-
log.info(resource.openStack.getStack(1), void 0, {
|
|
1047
|
-
F: __dxlog_file3,
|
|
1048
|
-
L: 85,
|
|
1049
|
-
S: void 0,
|
|
1050
|
-
C: (f, a) => f(...a)
|
|
1051
|
-
});
|
|
1052
|
-
log.info("\n", void 0, {
|
|
1053
|
-
F: __dxlog_file3,
|
|
1054
|
-
L: 86,
|
|
1055
|
-
S: void 0,
|
|
1056
|
-
C: (f, a) => f(...a)
|
|
1057
|
-
});
|
|
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
|
+
});
|
|
1058
934
|
}
|
|
1059
935
|
};
|
|
1060
936
|
if (enabled) {
|
|
@@ -1070,6 +946,9 @@ var DeferredTask = class {
|
|
|
1070
946
|
this._currentTask = null;
|
|
1071
947
|
this._nextTask = new Trigger();
|
|
1072
948
|
}
|
|
949
|
+
get scheduled() {
|
|
950
|
+
return this._scheduled;
|
|
951
|
+
}
|
|
1073
952
|
/**
|
|
1074
953
|
* Schedule the task to run asynchronously.
|
|
1075
954
|
*/
|
|
@@ -1184,6 +1063,173 @@ var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
|
|
|
1184
1063
|
});
|
|
1185
1064
|
};
|
|
1186
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);
|
|
1176
|
+
|
|
1177
|
+
// packages/common/async/src/sink.ts
|
|
1178
|
+
var sink = (emitter, event, count = 1) => {
|
|
1179
|
+
const [getPromise, resolve] = trigger();
|
|
1180
|
+
let counter = 0;
|
|
1181
|
+
const listener = () => {
|
|
1182
|
+
if (++counter === count) {
|
|
1183
|
+
emitter.off(event, listener);
|
|
1184
|
+
resolve();
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
emitter.on(event, listener);
|
|
1188
|
+
return getPromise();
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
// packages/common/async/src/stream-to-array.ts
|
|
1192
|
+
var streamToArray = (stream) => {
|
|
1193
|
+
let deferred;
|
|
1194
|
+
if (!stream.readable) {
|
|
1195
|
+
deferred = Promise.resolve([]);
|
|
1196
|
+
} else {
|
|
1197
|
+
deferred = new Promise((resolve, reject) => {
|
|
1198
|
+
if (!stream.readable) {
|
|
1199
|
+
return resolve([]);
|
|
1200
|
+
}
|
|
1201
|
+
let arr = [];
|
|
1202
|
+
const onData = (doc) => {
|
|
1203
|
+
arr?.push(doc);
|
|
1204
|
+
};
|
|
1205
|
+
const onEnd = (err) => {
|
|
1206
|
+
if (err) {
|
|
1207
|
+
reject(err);
|
|
1208
|
+
} else {
|
|
1209
|
+
resolve(arr);
|
|
1210
|
+
}
|
|
1211
|
+
cleanup();
|
|
1212
|
+
};
|
|
1213
|
+
const onClose = () => {
|
|
1214
|
+
resolve(arr);
|
|
1215
|
+
cleanup();
|
|
1216
|
+
};
|
|
1217
|
+
const cleanup = () => {
|
|
1218
|
+
arr = [];
|
|
1219
|
+
stream.removeListener("data", onData);
|
|
1220
|
+
stream.removeListener("end", onEnd);
|
|
1221
|
+
stream.removeListener("error", onEnd);
|
|
1222
|
+
stream.removeListener("close", onClose);
|
|
1223
|
+
};
|
|
1224
|
+
stream.on("data", onData);
|
|
1225
|
+
stream.on("end", onEnd);
|
|
1226
|
+
stream.on("error", onEnd);
|
|
1227
|
+
stream.on("close", onClose);
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
return deferred;
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1187
1233
|
// packages/common/async/src/test-stream.ts
|
|
1188
1234
|
import { Duplex } from "node:stream";
|
|
1189
1235
|
var TestStream = class extends Duplex {
|
|
@@ -1220,6 +1266,110 @@ var TestStream = class extends Duplex {
|
|
|
1220
1266
|
}
|
|
1221
1267
|
};
|
|
1222
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
|
+
|
|
1290
|
+
// packages/common/async/src/timer.ts
|
|
1291
|
+
var Timer = class {
|
|
1292
|
+
constructor(_callback) {
|
|
1293
|
+
this._callback = _callback;
|
|
1294
|
+
this._state = new Event();
|
|
1295
|
+
this._count = 0;
|
|
1296
|
+
}
|
|
1297
|
+
get state() {
|
|
1298
|
+
return this._state;
|
|
1299
|
+
}
|
|
1300
|
+
get running() {
|
|
1301
|
+
return !!this._timer;
|
|
1302
|
+
}
|
|
1303
|
+
start(options, cb) {
|
|
1304
|
+
if (isNaN(options.count) || isNaN(options.interval)) {
|
|
1305
|
+
throw new Error(`Invalid options: ${JSON.stringify(options)}`);
|
|
1306
|
+
}
|
|
1307
|
+
if (this.running) {
|
|
1308
|
+
this.stop();
|
|
1309
|
+
}
|
|
1310
|
+
const stop = () => {
|
|
1311
|
+
this.stop();
|
|
1312
|
+
cb?.();
|
|
1313
|
+
};
|
|
1314
|
+
const run = () => {
|
|
1315
|
+
if (this._count >= (options.count ?? 0)) {
|
|
1316
|
+
stop();
|
|
1317
|
+
} else {
|
|
1318
|
+
const interval = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);
|
|
1319
|
+
this._timer = setTimeout(async () => {
|
|
1320
|
+
await this._callback(this._count++);
|
|
1321
|
+
run();
|
|
1322
|
+
}, interval);
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
this._state.emit(true);
|
|
1326
|
+
this._count = 0;
|
|
1327
|
+
setTimeout(run);
|
|
1328
|
+
return this;
|
|
1329
|
+
}
|
|
1330
|
+
stop() {
|
|
1331
|
+
clearInterval(this._timer);
|
|
1332
|
+
this._timer = void 0;
|
|
1333
|
+
this._state.emit(false);
|
|
1334
|
+
return this;
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
// packages/common/async/src/until.ts
|
|
1339
|
+
var until = (cb, timeout) => {
|
|
1340
|
+
return new Promise((resolve, reject) => {
|
|
1341
|
+
const t = timeout && setTimeout(() => {
|
|
1342
|
+
reject(new Error(`Timeout after ${t}ms`));
|
|
1343
|
+
}, timeout);
|
|
1344
|
+
setTimeout(async () => {
|
|
1345
|
+
try {
|
|
1346
|
+
await cb((value) => {
|
|
1347
|
+
t && clearTimeout(t);
|
|
1348
|
+
resolve(value);
|
|
1349
|
+
}, (error) => {
|
|
1350
|
+
t && clearTimeout(t);
|
|
1351
|
+
reject(error);
|
|
1352
|
+
});
|
|
1353
|
+
} catch (err) {
|
|
1354
|
+
reject(err);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
});
|
|
1358
|
+
};
|
|
1359
|
+
var untilPromise = (cb) => cb();
|
|
1360
|
+
var untilError = (cb) => {
|
|
1361
|
+
return new Promise((resolve, reject) => {
|
|
1362
|
+
setTimeout(async () => {
|
|
1363
|
+
try {
|
|
1364
|
+
await cb();
|
|
1365
|
+
reject(new Error("No error was thrown."));
|
|
1366
|
+
} catch (err) {
|
|
1367
|
+
resolve(err);
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1223
1373
|
// packages/common/async/src/update-scheduler.ts
|
|
1224
1374
|
var TIME_PERIOD = 1e3;
|
|
1225
1375
|
var UpdateScheduler = class {
|
|
@@ -1301,6 +1451,7 @@ export {
|
|
|
1301
1451
|
MutexGuard,
|
|
1302
1452
|
Observable,
|
|
1303
1453
|
ObservableProvider,
|
|
1454
|
+
PersistentLifecycle,
|
|
1304
1455
|
PushStream,
|
|
1305
1456
|
TestStream,
|
|
1306
1457
|
TimeoutError,
|