KernelRouter.#deliverGCAction resolves the endpoint before tearing anything down. A vat absent from the kernel's vat table but not marked terminated is a vat between incarnations — restartVat takes it out of #vats for as long as launching a worker and negotiating with it takes — and releasing the kernel's side there would commit a disagreement the returning incarnation cannot reconcile.
It used to throw for that case, which left deliver by the run loop's catch and killed the run loop permanently. #1022 makes it { abort: true } instead: the rollback restores the c-list entries and the GC action, the kernel survives, and the returning incarnation is handed the action.
What is left is a spin. processGCActionSet is consulted first in #getNextRunQueueItem, so the restored action is reselected on the very next crank, and nothing in that crank awaits anything real. For the duration of the relaunch the run loop turns over as fast as the microtask queue allows, taking and rolling back a savepoint against SQLite each time.
It terminates — the restart finishes and the action is delivered — so this is a cost, not a wedge. Worth closing anyway. Two directions:
- Make restart a crank-scoped operation, as SwingSet does: an upgrade there is a run-queue item, so there is no window in which a vat is absent while the run loop runs. This removes the case rather than handling it.
- Give selection a way to skip an endpoint that has no handle, so the action stays in the durable set untouched instead of being consumed and restored every crank.
The first is the real fix and the larger one.
KernelRouter.#deliverGCActionresolves the endpoint before tearing anything down. A vat absent from the kernel's vat table but not marked terminated is a vat between incarnations —restartVattakes it out of#vatsfor as long as launching a worker and negotiating with it takes — and releasing the kernel's side there would commit a disagreement the returning incarnation cannot reconcile.It used to
throwfor that case, which leftdeliverby the run loop's catch and killed the run loop permanently. #1022 makes it{ abort: true }instead: the rollback restores the c-list entries and the GC action, the kernel survives, and the returning incarnation is handed the action.What is left is a spin.
processGCActionSetis consulted first in#getNextRunQueueItem, so the restored action is reselected on the very next crank, and nothing in that crank awaits anything real. For the duration of the relaunch the run loop turns over as fast as the microtask queue allows, taking and rolling back a savepoint against SQLite each time.It terminates — the restart finishes and the action is delivered — so this is a cost, not a wedge. Worth closing anyway. Two directions:
The first is the real fix and the larger one.