Trim per-release overhead in the scene

Only re-measure the viewport at begin() when a container is mounted, and
only allocate waiting machinery when an interrupted finish() actually
needs to wait for a later release. Interrupted releases now resolve with
the final decided outcome in every case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Faarh2Ts6StJPcz2mRiHiR
This commit is contained in:
Claude
2026-10-16 04:28:43 +00:00
parent a46c411ec4
commit 7033814550
+35 -28
View File
@@ -285,9 +285,12 @@ class Scene implements OriginScene {
*/ */
/** The latest release (finish call) of each operation. */ /** The latest release (finish call) of each operation. */
private readonly releases = new WeakMap<MutableOriginOperation, object>(); private readonly releases = new WeakMap<MutableOriginOperation, object>();
private readonly outcomes = new WeakMap< /** Final outcome of each operation, once a release has decided it. */
private readonly outcomes = new WeakMap<MutableOriginOperation, boolean>();
/** Interrupted releases waiting for a later release to decide. */
private readonly waiters = new WeakMap<
MutableOriginOperation, MutableOriginOperation,
{ promise: Promise<boolean>; resolve(committed: boolean): void } Array<(committed: boolean) => void>
>(); >();
private readonly effectSets = new WeakMap< private readonly effectSets = new WeakMap<
MutableOriginOperation, MutableOriginOperation,
@@ -322,7 +325,7 @@ class Scene implements OriginScene {
originKey: string, originKey: string,
action: OriginAction, action: OriginAction,
): Promise<OriginOperationHandle> { ): Promise<OriginOperationHandle> {
this.refreshViewport(); if (this.container) this.refreshViewport();
const operation = this.graph.begin( const operation = this.graph.begin(
originKey, originKey,
action, action,
@@ -383,36 +386,40 @@ class Scene implements OriginScene {
current, current,
); );
const outcome = this.outcomeOf(operation); // Another release already decided this operation.
const decided = this.outcomes.get(operation);
if (decided !== undefined) return decided;
// A later operation may already have finalized this edge. // A later operation may already have finalized this edge.
const finished = (operation.phase as OriginOperationPhase) === "finished"; const finished = (operation.phase as OriginOperationPhase) === "finished";
if (!this.graph.operation(operation.id) || finished) { if (!this.graph.operation(operation.id) || finished)
const committed = this.graph.operation(operation.id) return this.decide(
? operation.intent === "commit" operation,
: commit; finished ? operation.intent === "commit" : commit,
outcome.resolve(committed); );
return committed; // Caught by update() or superseded by a later finish(): wait for the
} // release that decides.
// Caught by update() or superseded by a later finish(). if (!current())
if (!current()) return outcome.promise; return new Promise<boolean>((resolve) =>
this.waiters.set(operation, [
...(this.waiters.get(operation) ?? []),
resolve,
]),
);
const committed = commit return this.decide(
? this.graph.commit(operation.id) operation,
: (this.graph.cancel(operation.id), false); commit
outcome.resolve(committed); ? this.graph.commit(operation.id)
return committed; : (this.graph.cancel(operation.id), false),
);
} }
/** Resolves with an operation's final outcome, whichever release decides. */ /** Record an operation's final outcome and settle interrupted releases. */
private outcomeOf(operation: MutableOriginOperation) { private decide(operation: MutableOriginOperation, committed: boolean) {
let outcome = this.outcomes.get(operation); this.outcomes.set(operation, committed);
if (!outcome) { for (const resolve of this.waiters.get(operation) ?? []) resolve(committed);
let resolve!: (committed: boolean) => void; this.waiters.delete(operation);
const promise = new Promise<boolean>((done) => (resolve = done)); return committed;
outcome = { promise, resolve };
this.outcomes.set(operation, outcome);
}
return outcome;
} }
registerElement(nodeKey: string, element: HTMLElement | null): void { registerElement(nodeKey: string, element: HTMLElement | null): void {