Skip to content

Commit 9eb6fc0

Browse files
authored
gpui: Fix RefCell panic in thermal/keyboard state callbacks (#49187)
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.
1 parent d89ee32 commit 9eb6fc0

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

crates/gpui/src/app.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -732,12 +732,15 @@ impl App {
732732
let app = Rc::downgrade(&app);
733733
move || {
734734
if let Some(app) = app.upgrade() {
735-
let cx = &mut app.borrow_mut();
736-
cx.keyboard_layout = cx.platform.keyboard_layout();
737-
cx.keyboard_mapper = cx.platform.keyboard_mapper();
738-
cx.keyboard_layout_observers
739-
.clone()
740-
.retain(&(), move |callback| (callback)(cx));
735+
// Use try_borrow_mut because this callback can be called asynchronously
736+
// from macOS while the app is already borrowed during an update.
737+
if let Ok(mut cx) = app.try_borrow_mut() {
738+
cx.keyboard_layout = cx.platform.keyboard_layout();
739+
cx.keyboard_mapper = cx.platform.keyboard_mapper();
740+
cx.keyboard_layout_observers
741+
.clone()
742+
.retain(&(), move |callback| (callback)(&mut cx));
743+
}
741744
}
742745
}
743746
}));
@@ -746,10 +749,13 @@ impl App {
746749
let app = Rc::downgrade(&app);
747750
move || {
748751
if let Some(app) = app.upgrade() {
749-
let cx = &mut app.borrow_mut();
750-
cx.thermal_state_observers
751-
.clone()
752-
.retain(&(), move |callback| (callback)(cx));
752+
// Use try_borrow_mut because this callback can be called asynchronously
753+
// from macOS while the app is already borrowed during an update.
754+
if let Ok(mut cx) = app.try_borrow_mut() {
755+
cx.thermal_state_observers
756+
.clone()
757+
.retain(&(), move |callback| (callback)(&mut cx));
758+
}
753759
}
754760
}
755761
}));

0 commit comments

Comments
 (0)