Skip to content

fix: the RefCell-across-a-GTK-call pattern also lives outside overlays/ — reactive_list.rs, calendar/tasks/connections rows, control-center, hytte-ui popup (#631 blast radius) #643

Description

@vibechoom

Found while sweeping #631 (the RefCell-borrow-held-across-a-GTK-call pattern). #631's fix covered nine sites; grepping the same two shapes turns up more, unfixed, outside that issue's file list. Filing rather than fixing — same as how #632/#638 spun out of earlier sweeps.

The pattern (unchanged from #631/#630/#627)

A RefCell borrow held across a GTK call that can emit a signal. If the emission is ever synchronous and the handler borrows the same cell, it panics — and a panic unwinding through a glib callback aborts the process. Reachability is not verified either way for any of these; that's the same standard #631 used, not a downgrade.

Form A — the borrow spans a loop of GTK teardown calls (.remove() / .destroy())

  • crates/hytte-ui/src/popup.rs:286 (close_catchers) — for win in catchers.borrow_mut().drain(..) { win.destroy(); }. Notable because it's in hytte-ui, not trollshell, and because fix(hytte-ui): destroy() the three remaining close()-teardown sites (#638) #639 already touched this exact function (converting close()destroy()) without addressing this borrow.
  • trollshell/src/components/reactive_list.rs:88for row in rows_track.borrow_mut().drain(..) { container_for_bind.remove_row(&row); }, inside the shared generic list-diff helper (RowContainer/bind_rows). This one backs 8 consumers (panels/{appearance,vpn,clipboard,displays,stats}.rs, panels/network/{wired,wifi,connection}.rs), so it's the highest-blast-radius instance found.
  • trollshell/src/panels/stats.rs:632 and trollshell/src/components/power_profile.rs:49 — same shape, each inside a bind() reactive closure with its own hand-rolled rows_track.
  • trollshell/src/widgets/calendar.rs:328for (_d, row) in rows_track.borrow_mut().drain(..) { group.remove(&row); }.
  • trollshell/src/widgets/tasks.rs:139for row in rows_track.borrow_mut().drain(..) { group.remove(&row); }.
  • trollshell/src/panels/connections.rs:67-72 — two sites, owned/others, structured as a named let mut owned = owned_for_bind.borrow_mut(); bound across the loop rather than an anonymous drain() temporary — same hazard, just spelled with an explicit binding instead of a chained call.
  • crates/trollshell-control-center/src/main.rs:449 (clear_rows) — for row in state.rows.borrow_mut().drain(..) { state.group.remove(&row); }. In the control-center binary, not the shell — the pattern isn't scoped to trollshell/src/overlays/.

Form B — if let Some(x) = cell.borrow_mut().take() with a GTK call in the then-block

  • trollshell/src/widgets/calendar.rs:331if let Some(p) = placeholder_track.borrow_mut().take() { group.remove(&p); }, immediately after the Form A site above in the same function.
  • trollshell/src/widgets/tasks.rs:142 and :145placeholder_track and overflow_track, same shape, same function as the Form A site above.
  • trollshell/src/panels/connections.rs:75overflow_for_bind, same shape, same function as the two Form A sites above.

Not sites (checked, ruled out)

Grepping .borrow_mut().take() and .borrow_mut().drain() more broadly also turns up several look-alikes that are not hazards, for the same reason #631 excluded prompt.rs's abort_subscription (no GTK call in the body) or because they use let-else (whose scrutinee temporary drops at the end of the let statement, not across a following GTK call, unlike a same-statement if let then-block):

  • trollshell/src/overlays/fullscreen.rs:60, trollshell/src/components/focus.rs:146, crates/trollshell-control-center/src/main.rs:114, trollshell/src/plugins/mod.rs:651, crates/hytte-ui/src/app.rs:80,95 — each either aborts a task/timer (no GTK) or uses let-else.

Recommendation

Same as #631: for the Form A sites, prefer RefCell::take() (or, where the container also needs post-drain access like connections.rs's named bindings, bind-then-loop) over holding the borrow across the removal calls; for Form B, bind-then-act. One mechanical PR per the cluster convention, reactive_list.rs first given its blast radius.

Refs #631, #630, #627, #632, #638.

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