Problem
perry-ui hosts have no idle-time GC integration: collections land on the AppKit/UIKit main thread mid-frame.
- The macOS app drives JS via an ~8 ms main-thread NSTimer pump calling
js_promise_run_microtasks() (perry-ui-macos/src/app.rs:565-575, 1224-1291); the pump tail runs the moving safepoint minor (promise/microtasks.rs:781-794).
- Any allocating widget callback can hit the alloc-point synchronous minor.
- A fallback minor of 300 ms+ freezes the UI for 20-40 frames; nothing consults frame deadlines, CADisplayLink idle time, or NSRunLoop activity.
- The budgeted embedder stepper already exists —
js_gc_step_us (gc/policy.rs:1834-1848) is a true time-boxed host API — but nothing wires it to the run loop.
Proposed fix
Expose js_gc_step_us to the UI pumps with a per-tick microbudget (e.g. spend idle time up to the next frame deadline), and gate the pump-boundary moving minor on remaining frame budget. Same pattern applies to perry-ui-ios/tvos/watchos hosts.
Context
2026-07-09 GC audit (platform-fit + pause dimensions). Depends partly on the incremental-GC graduation issue for the stepper to make real progress.
Problem
perry-ui hosts have no idle-time GC integration: collections land on the AppKit/UIKit main thread mid-frame.
js_promise_run_microtasks()(perry-ui-macos/src/app.rs:565-575, 1224-1291); the pump tail runs the moving safepoint minor (promise/microtasks.rs:781-794).js_gc_step_us(gc/policy.rs:1834-1848) is a true time-boxed host API — but nothing wires it to the run loop.Proposed fix
Expose
js_gc_step_usto the UI pumps with a per-tick microbudget (e.g. spend idle time up to the next frame deadline), and gate the pump-boundary moving minor on remaining frame budget. Same pattern applies to perry-ui-ios/tvos/watchos hosts.Context
2026-07-09 GC audit (platform-fit + pause dimensions). Depends partly on the incremental-GC graduation issue for the stepper to make real progress.