Skip to content

Stall sentinel counts OS timer deferral of an unfocused window as a main-thread stall #1482

Closed

Description

@mremond

Problem

perf/main-thread-stall is raised by the stall sentinel (apps/fluux/src/utils/stallSentinel.ts) when its 500 ms setInterval heartbeat fires more than 1 s late. The only background guard is document.hidden. On macOS, a window that is still visible but unfocused and idle keeps document.hidden === false, yet its timers can be deferred by the OS or WebKit. Those deferrals are recorded as main-thread stalls.

Evidence (Dev builds, macOS/WebKit, 1–21 September)

  • About 95% of the perf/main-thread-stall records share one signature: the latest focus/blur crumb is blur (often minutes old), with no crumb at all in the preceding 10 s and no perf:* crumb. For the 15–21 September builds, this holds for 29 of 29 records. None occurred while focused.
  • fluux.log has no application activity in the 10 s before 51 of 53 stall lines. There was often nothing for several minutes: no room switch, MAM catch-up or reconnect. The two exceptions are a wake-from-sleep resume.
  • There was no system sleep in that period, and the display was on at every stall.
  • For 49 of 53 stalls, RunningBoard shows the app UserInteractiveNonFocal and WindowServer reports the window AppVisible / FUSBProcessWindowState: visible, but without the AppDrawing assertion: the window is on screen and not drawing. The WebContent process stays UserInteractiveFocal.
  • One continuous run produced about 20 stalls in 3 minutes, up to 7.4 s each, with zero app log lines. It stopped the moment the window regained focus.
  • Normalised per background hour, the rate varies between builds without tracking code changes: 1.8/h on 8de4642c (Sept 7), 2.9/h on f822a994 (Sept 10), 0–0.8/h on the Sept 15–17 builds, 2.0–4.2/h on the Sept 18–21 builds. The recent increase is within historical variance, and no commit in cc7a7c64..804fd20e adds idle-time work.

The signal therefore mixes real main-thread blocks with OS or WebKit timer deferral of an unfocused, visually idle window, and the second class dominates.

Proposal

  1. Record whether the window had focus when the stall was measured, as a boolean ctx field (no route, no JID).
  2. Add a discriminating heartbeat in a dedicated Worker. If the Worker tick is late too, the process was throttled. If the Worker is on time while the main-thread tick is late, the main thread was genuinely blocked.
  3. Then reclassify or exclude the throttled case, so that perf/main-thread-stall only counts real blocks. Update the docs/ANOMALY_INVARIANTS.md entry: read the latest focus/blur crumb first; blur with no recent crumb points to OS deferral, not app work.

Separate and not covered here: an older class of focused stalls with perf:merge-archive crumbs seen on f822a994.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions