gpui: Fix RefCell panic in thermal/keyboard state callbacks (#49187)
morgankrey
created
The `on_thermal_state_change` and `on_keyboard_layout_change` callbacks
in `App::new_app()` called `borrow_mut()` unconditionally. These
callbacks are invoked asynchronously by macOS via dispatch queues when
the system's thermal or keyboard state changes, which can happen while
the app's RefCell is already borrowed during an update cycle, causing a
panic.
This change uses `try_borrow_mut()` instead. If the borrow fails
(because the app is already borrowed), the callback silently skips the
update - the state can be queried on the next frame.
Fixes [ZED-4WM](https://zed-dev.sentry.io/issues/ZED-4WM)
Closes #49181
Release Notes:
- Fixed a crash on macOS caused by thermal or keyboard layout state
changes occurring during UI updates.
@@ -732,12 +732,15 @@ impl App {
let app = Rc::downgrade(&app);
move || {
if let Some(app) = app.upgrade() {
- let cx = &mut app.borrow_mut();- cx.keyboard_layout = cx.platform.keyboard_layout();- cx.keyboard_mapper = cx.platform.keyboard_mapper();- cx.keyboard_layout_observers- .clone()- .retain(&(), move |callback| (callback)(cx));
+ // Use try_borrow_mut because this callback can be called asynchronously
+ // from macOS while the app is already borrowed during an update.
+ if let Ok(mut cx) = app.try_borrow_mut() {
+ cx.keyboard_layout = cx.platform.keyboard_layout();
+ cx.keyboard_mapper = cx.platform.keyboard_mapper();
+ cx.keyboard_layout_observers
+ .clone()
+ .retain(&(), move |callback| (callback)(&mut cx));
+ }
}
}
}));
@@ -746,10 +749,13 @@ impl App {
let app = Rc::downgrade(&app);
move || {
if let Some(app) = app.upgrade() {
- let cx = &mut app.borrow_mut();- cx.thermal_state_observers- .clone()- .retain(&(), move |callback| (callback)(cx));
+ // Use try_borrow_mut because this callback can be called asynchronously
+ // from macOS while the app is already borrowed during an update.
+ if let Ok(mut cx) = app.try_borrow_mut() {
+ cx.thermal_state_observers
+ .clone()
+ .retain(&(), move |callback| (callback)(&mut cx));
+ }
}
}
}));