Skip to content

refactor: replace AppProxy poll loop with MainThreadPoster#11

Closed
BumpyClock wants to merge 1 commit into
feat/app-platformfrom
feat/appproxy-poster
Closed

refactor: replace AppProxy poll loop with MainThreadPoster#11
BumpyClock wants to merge 1 commit into
feat/app-platformfrom
feat/appproxy-poster

Conversation

@BumpyClock

Copy link
Copy Markdown
Owner

Motivation

AppProxy scheduled cross-thread work onto the main thread by draining an
std::mpsc channel from a foreground task that woke every 16ms via a
background timer. The loop was flagged as a wart in the app-platform work: an
idle tray app pays a fixed wakeup cost forever, purely because the fork had no
dependency-free way to wake the main thread from a Send context. That is
exactly the battery profile a tray-first app is supposed to avoid.

The swap

The fork now exposes the primitive (gpui#7): App::main_thread_poster() hands
back a Clone + Send + Sync MainThreadPoster whose post() enqueues a
closure and wakes the run loop through the platform dispatcher — no polling, an
idle app stays parked.

  • AppProxy is now backed by the poster; the timer/drain loop and its
    gpui::Task (_drain_task) are deleted.
  • The shutdown boundary stays ours: a closed: AtomicBool, set at
    ShutdownRequested (which precedes app teardown), is checked in dispatch
    and re-checked inside every posted closure on the main thread. Because
    close() also runs on the main thread, any closure the pump dispatches after
    close() drops its payload — preserving the old drain loop's
    discard-after-close contract. The poster's own after-teardown false is a
    second layer, not a replacement.
  • The two old unit tests were locked to the mpsc channel internals; replaced
    with three #[gpui::test] behavior tests (runs-until-closed,
    queued-work-discarded-after-close, callback-shutdown-discards-following-work)
    driving a real &mut App. Adds gpui test-support to the crate's
    dev-dependencies (already used by crates/ui).

Pinned SHA

gpui / gpui_platform rev and the vendor/gpui submodule are pinned to
2ee4fa6587f9fc8eef012f86897034cb7d859d9d (main + 1, additive). The workspace
version and COMPATIBILITY.md bump are intentionally not done here — that
happens at merge time via /update-gpui discipline.

Merge preconditions

  1. gpui#7 merges → re-pin gpui/gpui_platform + submodule to the merge SHA
    via /update-gpui.
  2. PR#8 merges → rebase this branch onto the updated feat/app-platform.

Gates

  • cargo test -p gpui-component-app — 49 unit + headless + doc, all green
  • cargo clippy --workspace --all-targets -- --deny warnings — clean
  • cargo fmt --check — clean
  • app_shell_tray --smoke and app_shell --smoke — both exit 0

AppProxy scheduled cross-thread work onto the main thread by draining an
mpsc channel from a foreground task that woke every 16ms via a background
timer. That poll loop was flagged as a wart: an idle tray app paid a fixed
wakeup cost forever, purely because the fork had no dependency-free way to
wake the main thread from a Send context.

The fork now provides that primitive. App::main_thread_poster hands back a
Clone+Send+Sync MainThreadPoster whose post() enqueues a closure and wakes
the run loop through the platform dispatcher, so an idle app stays parked.

Back AppProxy with the poster and delete the timer loop. The shutdown
boundary stays ours: a `closed` AtomicBool, set at ShutdownRequested (which
precedes app teardown), is checked in dispatch and re-checked inside each
posted closure on the main thread. Since close() also runs on the main
thread, any closure the pump dispatches after close() drops its payload,
preserving the old drain loop's discard-after-close contract. The poster's
own after-teardown false is a second layer, not a replacement.

Pins gpui/gpui_platform and the vendor submodule to 2ee4fa6 (gpui#7). The
workspace version and COMPATIBILITY.md bump is deferred to merge time via
/update-gpui discipline.
@coderabbitai

coderabbitai Bot commented Jul 17, 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

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e65abb2e-b23e-47c3-aa5d-db1a63ab24fb

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/appproxy-poster

Comment @coderabbitai help to get the list of available commands.

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