Skip to content

perry-ui-windows-winui: callback pointers copied into Reactor closures escape the root scanner #8734

Description

@proggeramlug

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions