fix(platform): close startup subscription handoff gaps
This commit is contained in:
@@ -138,20 +138,20 @@ export class PlatformRuntime {
|
||||
const generation = this.startupGeneration + 1;
|
||||
this.startupGeneration = generation;
|
||||
this.lifecycle = 'starting';
|
||||
this.options.scene.showLoading();
|
||||
this.requireActiveStartup(generation);
|
||||
const resourcesReady = this.beginReadyTask(
|
||||
'resources',
|
||||
this.options.loadResources,
|
||||
generation,
|
||||
);
|
||||
const minimumDisplayReady = this.beginReadyTask(
|
||||
'minimum-display',
|
||||
this.options.waitForMinimumDisplay,
|
||||
generation,
|
||||
);
|
||||
|
||||
try {
|
||||
this.options.scene.showLoading();
|
||||
this.requireActiveStartup(generation);
|
||||
const resourcesReady = this.beginReadyTask(
|
||||
'resources',
|
||||
this.options.loadResources,
|
||||
generation,
|
||||
);
|
||||
const minimumDisplayReady = this.beginReadyTask(
|
||||
'minimum-display',
|
||||
this.options.waitForMinimumDisplay,
|
||||
generation,
|
||||
);
|
||||
this.requireActiveStartup(generation);
|
||||
const config = await this.options.resolveRuntimeConfig();
|
||||
this.requireActiveStartup(generation);
|
||||
@@ -204,7 +204,7 @@ export class PlatformRuntime {
|
||||
this.router = router;
|
||||
this.wire = wire;
|
||||
this.requireActiveStartup(generation);
|
||||
this.unsubscribeWire = wire.subscribe((event) => { this.onWireEvent(event); });
|
||||
this.subscribeToWire(wire, generation);
|
||||
this.requireActiveStartup(generation);
|
||||
this.markReady('config');
|
||||
this.lifecycle = 'running';
|
||||
@@ -443,6 +443,49 @@ export class PlatformRuntime {
|
||||
wire.stop();
|
||||
}
|
||||
|
||||
private subscribeToWire(wire: PlatformWireClient, generation: number): void {
|
||||
let actualUnsubscribe: (() => void) | null = null;
|
||||
let releaseRequested = false;
|
||||
let released = false;
|
||||
const temporaryOwner = (): void => {
|
||||
if (released) return;
|
||||
if (actualUnsubscribe === null) {
|
||||
releaseRequested = true;
|
||||
return;
|
||||
}
|
||||
released = true;
|
||||
const unsubscribe = actualUnsubscribe;
|
||||
actualUnsubscribe = null;
|
||||
unsubscribe();
|
||||
};
|
||||
|
||||
this.unsubscribeWire = temporaryOwner;
|
||||
const returnedUnsubscribe: unknown = wire.subscribe(
|
||||
(event) => { this.onWireEvent(event); },
|
||||
);
|
||||
if (typeof returnedUnsubscribe !== 'function') {
|
||||
throw new TypeError(
|
||||
'WireClient.subscribe must return an unsubscribe function',
|
||||
);
|
||||
}
|
||||
actualUnsubscribe = returnedUnsubscribe as () => void;
|
||||
if (!releaseRequested) return;
|
||||
|
||||
const cleanupErrors: unknown[] = [];
|
||||
this.captureCleanupError(cleanupErrors, temporaryOwner);
|
||||
if (cleanupErrors.length === 0) this.requireActiveStartup(generation);
|
||||
|
||||
const primary = this.fatalError
|
||||
?? new Error(`PlatformRuntime startup interrupted while ${this.lifecycle}`);
|
||||
const failure = this.failureError(
|
||||
'subscription handoff',
|
||||
primary,
|
||||
cleanupErrors,
|
||||
);
|
||||
if (this.fatalError !== null) this.fatalError = failure;
|
||||
throw failure;
|
||||
}
|
||||
|
||||
private captureCleanupError(errors: unknown[], operation: () => void): void {
|
||||
try {
|
||||
operation();
|
||||
|
||||
Reference in New Issue
Block a user