Skip to content

renderer, termio: count each surface's thread wakes by source - #73

Merged
lawrencecchen merged 2 commits into
release/cmux-9ce9b7d0-e2from
cmux/surface-wake-counters
Nov 3, 2026
Merged

lawrencecchen merged 2 commits into
release/cmux-9ce9b7d0-e2from
cmux/surface-wake-counters

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

ghostty_surface_render_counters gains seven fields at the end of the struct. They count each surface's thread wakes by source:

  • renderer thread: the wakeup async, the draw-now async, and the cursor blink, animation and scrollback-compression timer fires
  • termio thread: the wakeup async, and its timer fires (resize coalesce, synchronized-output reset, selection scroll)

Each count is one relaxed atomic add in a callback that already runs. No behavior changes.

Why

cmux keeps hidden terminals live (its warm set). On cmux24, 24 busy hidden terminals at a warm cap of 25 woke the app 181.8 times a second, against 76.9 at a cap of 8. None of those wakeups were on the main thread.

cmux's own counters show that its output path already runs once per hidden output batch. So the remaining cost, about 2.4-3.4 wakeups a second while idle plus 3-4 per batch for each live hidden terminal, is on the surface's own threads. These counts name the source, per surface, so a cmux test can bound it exactly (cmux SWITCH FRAMES, "hidden output" row).

Base

This is based on release/cmux-9ce9b7d0-e2 (34e73ba8b), the cmux pin, so that one GhosttyNextKit pin bump takes only this change. The struct grows at its end; the existing fields keep their offsets.

Test plan

  • Fork CI builds the xcframework for this PR.
  • The cmux red test (branch fix/hidden-terminal-wakeups) reads these counts for live hidden surfaces.

Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Adds per-surface thread wake counters to ghostty_surface_render_counters so a cmux test can bound the cost of keeping hidden terminals alive. Each counter is a relaxed atomic add in an existing callback; no behavior changes.

  • Renderer thread counters: wakeup async, draw-now async, and cursor blink, animation, and scrollback-compression timer fires.
  • Termio thread counters: wakeup async and timer fires (resize coalesce, synchronized-output reset, selection scroll).

Written for commit 782f66b. Summary will update on new commits.

View guided diff Turn on auto-fix

ghostty_surface_render_counters gains seven fields at its end: the renderer
thread's wakeup async, draw-now async, cursor blink, animation and
scrollback-compression timer fires, and the termio thread's wakeup async and
timer fires.

Why: cmux keeps hidden terminals live (its warm set). A live hidden surface
costs about 2.4-3.4 process wakeups a second while idle and 3-4 more per
output batch, on threads cmux's own counters do not see. These counts tell
which source it is, per surface, so a test can bound them exactly.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e7edc020-aa46-433c-8f9c-e607ef00a587

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (subagent, correctness first): SHIP at 9509f26

  • ABI: the 16 existing fields keep their order and offsets. The 7 new uint64 fields are appended in the same order in ghostty.h and the Zig extern struct, and the getter fills all 23 fields.
  • Each counter counts once per real wake. On the renderer thread, the timer callbacks count after the Canceled return. On the termio thread, the timers are never cancelled today; libxev's timer_reset re-queues without a cancel callback.
  • Threads: the atomics are relaxed. io_thread and renderer are stable fields of the heap-allocated CoreSurface, so they are read under the same lifetime rule as the existing counters.
  • No behavior change: the change adds fetchAdd calls only.
  • Note: the struct is returned by value. The header and the library must ship together; GhosttyKit always ships them together.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (subagent, correctness first): SHIP at 782f66b

@lawrencecchen
lawrencecchen merged commit 2122672 into release/cmux-9ce9b7d0-e2 Nov 3, 2026
9 checks passed
lawrencecchen added a commit that referenced this pull request Nov 4, 2026
The release line moved after the first merge. Keep its head an ancestor of main.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant