The serialization lock. Read through phase where the kind matters.
A refinement run is live. Not the same as phase === 'refining': the final
phase's release opens the lock while the run is still unwinding, and a kick
landing in that window must still read busy.
PrivateretryingPrivaterequestPrivate ReadonlyrequestPrivatepassGeneration of the view pass now running (0 while none is).
PrivatecompletedNewest generation whose view pass has finished.
PrivatepassPrivatependingTargeted resyncs that arrived while a pass was running (folded in after it).
Resync paths handed to the follow-up pass with the pending state they queued.
PrivatependingThe pending slot holds the empty state a resync queued (mergeable).
performance.now() of the last committed view pass (B5).
PrivatepassPrivatepassPrivateholdAbort controller of the work holding the lock: the running pass, or the refinement run that published its own. Aborted by a superseding view, a pre-empting retry and dispose.
PrivateretryPrivatekickPrivate ReadonlyhostThe current lock state; see the module doc.
A view pass is running or queued — the data for the requested view has not landed. A refinement drain does not count (it runs after the commit); a queued state does, from the moment it is parked until a pass takes it.
Every generation minted so far has finished a view pass.
A failed-loader retry is waiting for the refinement run to hand it the lock.
A lock-busy refinement re-check is scheduled.
Gate a view request on the lock. Returns null when the lock is free: the
caller runs the pass now (beginPass). Otherwise the request is
parked, joined or queued, and the returned promise settles when a pass of
its generation (or newer) has committed.
PrivateparkA targeted resync carries no new view state, so it never supersedes. During a refinement hold it queues an empty state (merged with an earlier resync's, dropped under a real pending view, which sweeps a superset); mid-pass or mid-retry it is parked and folded in once the pass or retry ends.
PrivatecanPrivaterunningWhether a superseding view must let the running VIEW pass commit rather than
abort it (B5): the pass chain has gone DRAG_COMMIT_INTERVAL_MS
without a commit and the pass is younger than DRAG_COMMIT_MAX_HOLD_MS.
Only a running view pass can be owed anything (A8): across a frame yield,
a refinement drain or a retry the timing fields describe a pass that has
ended. A displayDims change never holds — geometry projected for the old
axes is not a truthful frame of a drag.
PrivatearmAbort the held pass once its DRAG_COMMIT_MAX_HOLD_MS runs out, so the view queued behind it runs even when no newer view arrives to re-check the cap. One timer per pass; a no-op if the pass ended, or nothing is pending, by the time it fires.
PrivateclearTake the lock for a view pass of viewState: its generation is the one it
was queued with, else a fresh one (a direct call, which also starts a new
drag chain). Stale resync stashes and hold timers die here.
The running pass committed its frame (B5 commit clock).
Close a view pass. A pass that committed settles the waiters it covers; a superseded one parks the direct caller until the pass that supersedes it commits (the returned promise, for the caller to await). Then decide what runs next.
PrivateabandonArchive fault: nothing queued can run; release everything.
PrivatenextAfter a view pass: a pending state runs after one frame; else refinement drains the ladders; else the lock is released. Resyncs parked mid-pass ride an empty pending state unless a real one (a superset sweep) is queued.
PrivatefoldFold resyncs parked while the lock was held into an empty pending state — dropped instead under a real pending view, which sweeps a superset.
PrivateyieldHold the lock across one frame (so at least one frame paints between
passes), then release it and run the NEWEST pending state. The state stays
in the slot across the yield (A8): a view arriving meanwhile supersedes it
through request, so the newest state runs and the resync-stash
rules apply unchanged. scheduleFrame degrades to a timer in hidden tabs
and runs synchronously without rAF.
PrivatereenterRe-enter a drained pending state, consuming the resync paths stashed for it — the ONLY way a pending state may be re-entered, or the paths leak into a later, unrelated same-view pass and narrow it.
PrivatewaitSettle the waiters covered by generation upToGen (all of them by default
— dispose / archive-fault flushes); newer ones stay parked.
Settle the waiters the newest finished view pass covers.
Run the refinement orchestrator under the lock (taken here if not already
held). An error escaping the orchestrator glue — each loop releases the
lock in its own finally, so this is a double fault — releases the lock
and drains whatever queued meanwhile, or the viewer would freeze.
A disposed or archive-faulted loader runs no drain: the lock is released (and what queued drained) instead. The post-load kick calls in unconditionally, and a lazy LOD level can latch the fault during the load — a lock taken for a run that never starts would be held forever, parking every view and refusing every retry.
Kick refinement from OUTSIDE a pass (a deferred subtree registered loaders
mid-session, a retry committed part of a ladder, an abandoned-rung re-drain).
Busy — the lock held, or a run still unwinding — re-checks on a plain timer
(one at a time; scheduleFrame would recurse synchronously without rAF).
A refinement run starts, publishing its own abort controller.
A refinement run unwound. A retry that pre-empted it inherits the lock,
unless the lock went to a view hand-off (lockHandedOff) or a dispose.
A refinement run cancelled by a pending view hands the lock over across one frame. The state it took goes back into the slot until the frame fires (A8) — synchronous with the loop's take, so nothing can interleave.
The refinement run's final release: open the lock and drain a state queued during the last pass (else settle the waiters). A pre-empting retry takes the lock over in endRefinement instead.
Release the lock and run what queued while it was held — or, with nothing queued, settle the parked waiters (a drained state's own pass settles them at its commit; settling here too would release the pacing gate early).
Re-enter a state queued while the lock was held (on a microtask, see
ViewStateQueue.drain), with its stashed resync paths.
Whether a state was queued (its pass then settles the waiters).
Take the lock for a failed-loader retry (A10). A free lock is taken at once. A refinement drain holding it, with no view queued to run next, is pre-empted the way a view change pre-empts it — its reads are aborted and it hands the lock over as it unwinds. A view pass holding it (or one queued behind the drain) keeps it: the retry is reported deferred.
The pending check is made HERE, once: a view arriving after the pre-emption queues behind the retry (one loader's retry pass) instead of taking the lock from it. Yielding would turn a retry already accepted into a deferred one the user has to repeat, for a wait the size of one pass.
Whether the caller now holds the lock (release with releaseRetry).
Release a lock taken by acquireForRetry. A resync parked during the retry is folded into the pending slot for the caller's drainPending, or dropped when the caller is about to reload the whole view (a superset).
PrivatesettleRelease everything for a disposed loader: timers, the lock, the pending slot and resync stashes. Every waiter settles (resolve-only, never reject) so no caller hangs across a dataset switch. The controller is left for the host's resource teardown to abort.
Owner of the view-pass lock, the pending slot's lifecycle and refinement hand-offs.