You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Landed with #8722 (via #8733) and documented at both scanners, but not fixed there.
perry-ui-windows-winui now registers a GC root scanner over its NODES / ON_ACTIVATE / ON_TERMINATE / PENDING_TIMERS tables, so the stored callback pointers are marked and rewritten. That is not sufficient, because several paths copy the unboxed pointer out of the scanned table and into a boxed Rust closure the collector cannot reach:
render_handle operates on a clone of the node and moves the raw callback pointer into move closures owned by the Reactor element tree.
start_runtime_pump does the same for PENDING_TIMERS into DispatcherTimer closures.
app_run does the same for ON_TERMINATE into an on_exit closure.
Those copies live inside Box<dyn Fn…> allocations that no root scanner can visit or rewrite, so after an evacuating collection they point into from-space. The failure mode is the usual one for this class: nothing at collection time, then TypeError: value is not a function (or a segfault) whenever the widget/timer/exit hook next fires.
Scanning the tables cannot fix this. The fix is to stop capturing the value and instead capture a key, then re-read the scanned slot at invoke time — the indirection perry-ui-macos already gets from its handle-keyed maps. That is roughly a 15-site refactor across widgets.rs and app.rs.
Two notes on scope:
This is a pre-existing property of the WinUI backend's design, not something the scanner work introduced. Before feat(windows): complete WinUI 3 backend #8722 the tables were not scanned at all, so this was strictly worse.
It cannot be reproduced or verified from macOS — the crate is Windows-only and a cross-compile fails on a C build script (perry-audio-miniaudio needs Windows headers). Whoever picks this up will need a Windows host, and PERRY_GC_SCHEDULE_SEED + PERRY_GC_PROTECT_FROMSPACE are the instruments that would turn it into an immediate fault rather than a delayed one.
Landed with #8722 (via #8733) and documented at both scanners, but not fixed there.
perry-ui-windows-winuinow registers a GC root scanner over itsNODES/ON_ACTIVATE/ON_TERMINATE/PENDING_TIMERStables, so the stored callback pointers are marked and rewritten. That is not sufficient, because several paths copy the unboxed pointer out of the scanned table and into a boxed Rust closure the collector cannot reach:render_handleoperates on a clone of the node and moves the raw callback pointer intomoveclosures owned by the Reactor element tree.start_runtime_pumpdoes the same forPENDING_TIMERSintoDispatcherTimerclosures.app_rundoes the same forON_TERMINATEinto anon_exitclosure.Those copies live inside
Box<dyn Fn…>allocations that no root scanner can visit or rewrite, so after an evacuating collection they point into from-space. The failure mode is the usual one for this class: nothing at collection time, thenTypeError: value is not a function(or a segfault) whenever the widget/timer/exit hook next fires.Scanning the tables cannot fix this. The fix is to stop capturing the value and instead capture a key, then re-read the scanned slot at invoke time — the indirection
perry-ui-macosalready gets from its handle-keyed maps. That is roughly a 15-site refactor acrosswidgets.rsandapp.rs.Two notes on scope:
perry-audio-miniaudioneeds Windows headers). Whoever picks this up will need a Windows host, andPERRY_GC_SCHEDULE_SEED+PERRY_GC_PROTECT_FROMSPACEare the instruments that would turn it into an immediate fault rather than a delayed one.