Skip to content

fixed main thread hangs - #2619

Merged
RaajeevChandran merged 22 commits into
mainfrom
fix/app-hangs-09-03
Sep 4, 2026
Merged

fixed main thread hangs#2619
RaajeevChandran merged 22 commits into
mainfrom
fix/app-hangs-09-03

Conversation

@RaajeevChandran

Copy link
Copy Markdown
Contributor

Summary

Twenty-one main-thread fixes from a Sentry app-hang triage. Every fix targets a verified main-thread stack (disk, keychain, database queue, or CPU work reached from a view body or the launch path), and each lands as its own commit so it can be bisected or reverted independently.

Launch

  • The chat sessions manager's first metadata read is warmed through the database queue off the main actor before the speculative ChatView prewarm, so the prewarm no longer parks main behind a busy serial queue. Gated on storage readiness, matching the existing ChatView prewarm.
  • Highlightr's JSContext (highlight.js) is warmed on a background thread during launch settle instead of booting on main when the first code block renders.
  • The agent-secret account memo is seeded at launch so headless composers (HTTP, subagents, channels) never hit a cold keychain enumeration on main.

Chat and composer

  • The voice button reads the speech service's published mic-permission mirror instead of a synchronous TCC round-trip on every body evaluation.
  • The swap-emulation flag file is read on a background queue and memoized, so the 2s memory tick no longer touches disk on main.
  • Chat model candidates are stored on the picker cache and recomputed per rebuild instead of string-filtered on every render.
  • Token estimation uses UTF-8 byte length instead of grapheme counting, making the per-render context-budget math O(1) instead of O(conversation).
  • Agent secret name listing on the compose path is non-blocking: a warm memo answers directly, a cold one kicks a single-flight background seed. The memo is maintained in place on save and delete so a store_secret mid-turn never causes the next send to see zero secrets.
  • Live diff previews scan at most 256 KB of the streaming arg buffer per delta (byte-bounded), turning a quadratic total into a bounded one. Mid-stream syntax highlighting is capped by size with one plain rebuild at the crossing so later deltas don't inherit the last token's color.

Config apply and providers

  • Keychain secret references resolve off the cooperative executor during declarative config apply. All resolutions are hoisted before the provider read-mutate-write so the suspension can never revert a concurrent Settings edit, and the remote-provider path re-fetches the live provider after the await.
  • The relay WebSocket session (which reads the disk-backed proxy config) is built in a detached task. Connect re-checks by task identity across its suspensions and retires a stale socket safely, and the receive loop only tears down or feeds the current socket.

Downloads

  • Download containment probes, resume size checks, the manifest completion check, and the compatibility report all run detached instead of on the main actor. The orchestration task inherits main-actor isolation from its service, so this code was on main despite reading like background work.

Quit

  • The seven background-writer flushes in applicationWillTerminate drain concurrently under one bounded wait instead of serially on main. Keychain and config-disk drains get an explicit sub-cap so a slow securityd write still lands before the hard exit, and the group deadline stays under the app-hang watchdog.

Menu bar

  • Status-item refreshes are coalesced to one pass per runloop turn instead of one per Combine publish.

Verification

  • Builds green locally. Nothing here changes KV-cache prefix bytes: the prompt prefix hasher was deliberately left alone.
  • Sentry: 30 issues marked resolved in next release with mechanism-naming reasons, 93 archived until-escalating after stack verification (zero first-party frames or swap-stall samples). Resolved issues auto-reopen as regressions if they fire after this ships.

Changes

  • Behavior change
  • UI change (screenshots below)
  • Refactor / chore
  • Tests
  • Docs

Checklist

  • I have read CONTRIBUTING.md
  • I added/updated tests where reasonable
  • I updated docs/README as needed
  • I verified build on macOS with Xcode 16.4+

@github-actions github-actions Bot added the bug Something isn't working label Sep 3, 2026
@RaajeevChandran
RaajeevChandran merged commit 97f5cc1 into main Sep 4, 2026
8 checks passed
@RaajeevChandran
RaajeevChandran deleted the fix/app-hangs-09-03 branch September 4, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant