feat(ui): wire GC pump-step + OS memory-pressure into non-macOS hosts (#6183, #6184)#6210
Conversation
…#6183, #6184) Extend the GC embedder integration (already merged for macOS) to the other UI hosts. 2026-07-09 GC audit. Pump GC-step (#6183): call js_gc_step_us(2_000, null) in each host's run-loop / timer pump tail — a precise-root safepoint where the JS stack is unwound — so an active budgeted GC cycle drains in bounded 2ms slices instead of an unbounded alloc-point collection landing on the UI thread mid-interaction: - iOS / tvOS / visionOS: PerryPumpTarget pump: (extern block + call) - gtk4: glib timeout pump closure - Windows: Win32 message-loop timer-tick block - Android: nativePumpTick JNI callback - watchOS: SwiftUI render Timer (Swift @_silgen_name FFI) OS memory-pressure (#6184): forward the platform signal to js_gc_memory_pressure (1 = minor, 2 = full collect + trigger clamp): - iOS / tvOS: PerryAppDelegate applicationDidReceiveMemoryWarning: -> 2 - visionOS: SwiftUI host observes UIApplication.didReceiveMemoryWarning -> 2 - macOS: new memory_pressure.rs installs a DISPATCH_SOURCE_TYPE_MEMORYPRESSURE source on the main queue at app_run (WARN -> 1, CRITICAL -> 2) - Android: PerryActivity.onLowMemory -> 2 and new onTrimMemory(level) mapping (RUNNING_CRITICAL / COMPLETE / MODERATE -> 2, lighter trims -> 1) via a new nativeMemoryPressure JNI shim + Kotlin external fun Linux PSI polling for gtk4 is intentionally out of scope (a server concern).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (14)
📝 WalkthroughWalkthroughThis PR adds a bounded embedder GC step (js_gc_step_us, 2ms budget) invoked at each UI pump tick across Android, GTK4, iOS, tvOS, visionOS, watchOS, and Windows, and wires OS memory-pressure signals (Android JNI, iOS/tvOS memory warnings, macOS libdispatch source, visionOS notifications) into js_gc_memory_pressure. ChangesEmbedder GC pump-step and memory-pressure wiring
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Extends the GC embedder integration already merged for the macOS host to the
remaining UI hosts. Two runtime FFIs land on
mainalready; this wires thehost-side calls. Reference: #6183 #6184; 2026-07-09 GC audit.
A. Pump GC-step (#6183)
Mirrors the merged macOS pump call:
js_gc_step_us(2_000, null)in each host'srun-loop / timer pump tail. That point is a precise-root safepoint (the JS
stack is fully unwound), so an active budgeted GC cycle drains in bounded 2ms
slices instead of an unbounded alloc-point collection landing on the UI thread
mid-interaction. Extern declared per host, matching the macOS style.
PerryPumpTarget::pump:(perry-ui-ios/src/app.rs)PerryPumpTarget::pump:(perry-ui-tvos/src/app.rs)PerryPumpTarget::pump:(perry-ui-visionos/src/app.rs)TimerinPerryWatchApp.swift(@_silgen_nameFFI)glib::timeout_add_localpump closure (perry-ui-gtk4/src/app.rs)TIMER_TICK_NEEDEDblock (perry-ui-windows/src/app.rs)nativePumpTickJNI callback (perry-ui-android/src/app.rs)B. OS memory-pressure (#6184)
Forwards the platform signal to
js_gc_memory_pressure(level)(1 = minor,2 = full collect + trigger clamp).
applicationDidReceiveMemoryWarning:to the RustPerryAppDelegate(define_class!), callingjs_gc_memory_pressure(2)(aUIKit memory warning is the last notice before jetsam → critical).
PerryVisionBootstrapobservesUIApplication.didReceiveMemoryWarningNotificationon the main queue → level 2.
memory_pressure.rsinstalls aDISPATCH_SOURCE_TYPE_MEMORYPRESSUREsource on the main queue atapp_run:WARN → 1,CRITICAL → 2. libdispatch symbols bound directly (part oflibSystem; no new crate). This is the one macOS piece that wasn't done yet.
PerryActivity.onLowMemory() → 2; newonTrimMemory(level)override maps
TRIM_MEMORY_RUNNING_CRITICAL | COMPLETE | MODERATE → 2andlighter trims → 1 (constants aren't monotonic by severity, so the severe set
is matched explicitly). Wired via a new
nativeMemoryPressureJNI shim(
perry-ui-android/src/lib.rs) + Kotlinexternal fun(PerryBridge.kt),guarded against
UnsatisfiedLinkErrorfor warnings that arrive pre-init. Theexisting Google-Maps
forwardMapsLifecycle("lowMemory")is preserved.Out of scope
wired (only the pump-step). Notable as remaining if desired later
(
WM_COMPACTING/QueryMemoryResourceNotificationon Windows).Build verification
Verified with target-specific
cargo checkwhere the toolchain is present;inspection-only where a cross native-toolchain is missing on this macOS host
(honest status — no fabricated builds):
cargo check -p perry-ui-macos— PASS (includes dispatch-source module)cargo check --target aarch64-apple-ios— PASScargo check --target aarch64-apple-tvos— PASScargo check --target aarch64-apple-visionos— PASS (Rust; Swift template inspection-only)PerryWatchApp.swift), inspection-onlyglib-syspkg-config can't cross-compile)libmimalloc-sysneeds a Windows C++ toolchain)aarch64-linux-android-clangNDK not installed); Kotlin inspection-onlycargo fmt --all -- --checkpasses.Summary by CodeRabbit