fix(desktop): cancel the pending commit-cost measurement rAF

Follow-up to #77652: each runFlush registered a fresh requestAnimationFrame and never cancelled it. Chromium parks rAF callbacks for hidden renderers, so a long hidden stream at the 33ms floor accumulates thousands of parked closures that all fire in the first frame on refocus (all but one no-oping through the stale-frame guard). Track the pending handle, cancel it before requesting a new one (only the newest flush's measurement matters), and cancel on unmount.
This commit is contained in:
kshitij 2026-08-03 18:24:26 +05:30 committed by kshitij
parent 1f1acc0e4d
commit c4ac62a7ee
1 changed files with 19 additions and 1 deletions

View File

@ -188,6 +188,9 @@ export function useMessageStream({
// What the previous flush cost on the main thread — drives the adaptive
// flush floor in scheduleDeltaFlush so multi-stream load yields to input.
const lastFlushCostRef = useRef<number>(0)
// The pending commit-cost measurement rAF, so a newer flush (or unmount)
// can cancel it instead of letting parked callbacks pile up while hidden.
const measureRafRef = useRef<number | null>(null)
const nativeSubagentSessionsRef = useRef<Set<string>>(new Set())
// Turns that auto-compacted: skip post-turn hydrate so live scrollback survives.
const compactedTurnRef = useRef<Set<string>>(new Set())
@ -284,7 +287,16 @@ export function useMessageStream({
// stays as the fallback.
const writeCost = performance.now() - startedAt
lastFlushCostRef.current = writeCost
window.requestAnimationFrame(frameStart => {
// At most one measurement rAF may be pending: only the newest flush's
// measurement matters (the guard below discards stale frames), and a
// hidden renderer parks rAF callbacks — without cancellation a long
// hidden stream at the floor would accumulate thousands of parked
// closures that all fire in the first frame on refocus.
if (measureRafRef.current !== null) {
window.cancelAnimationFrame(measureRafRef.current)
}
measureRafRef.current = window.requestAnimationFrame(frameStart => {
measureRafRef.current = null
// A newer flush already started; its own measurement wins.
if (lastFlushAtRef.current !== startedAt) {
return
@ -334,6 +346,12 @@ export function useMessageStream({
}
flushHandleRef.current = null
if (measureRafRef.current !== null && typeof window !== 'undefined') {
window.cancelAnimationFrame(measureRafRef.current)
}
measureRafRef.current = null
flushQueuedDeltas()
},
[flushQueuedDeltas]