Skip to content

Commit 99a558f

Browse files
authored
gpui: Defer thermal/keyboard state updates when app is borrowed (#49189)
Follow-up to #49187. Instead of silently skipping updates when `try_borrow_mut` fails, this PR defers the update by spawning it on the foreground executor. This ensures the state change is eventually processed after the current borrow completes. Release Notes: - N/A
1 parent dd836fc commit 99a558f

File tree

2 files changed

+30
-16
lines changed

2 files changed

+30
-16
lines changed

crates/gpui/.rules

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# GPUI crate-specific rules
2+
#
3+
# This file is non-exhaustive. Check the root .rules for general guidelines.
4+
5+
# Platform callbacks
6+
7+
* Platform callbacks (e.g., `on_keyboard_layout_change`, `on_thermal_state_change`) can fire
8+
asynchronously from macOS while `AppCell` is already borrowed. Defer work via
9+
`ForegroundExecutor::spawn` rather than borrowing `AppCell` directly in the callback body.

crates/gpui/src/app.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ impl App {
654654
) -> Rc<AppCell> {
655655
let background_executor = platform.background_executor();
656656
let foreground_executor = platform.foreground_executor();
657+
let callback_executor = foreground_executor.clone();
657658
assert!(
658659
background_executor.is_main_thread(),
659660
"must construct App on main thread"
@@ -730,32 +731,36 @@ impl App {
730731

731732
platform.on_keyboard_layout_change(Box::new({
732733
let app = Rc::downgrade(&app);
734+
let executor = callback_executor.clone();
733735
move || {
734736
if let Some(app) = app.upgrade() {
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-
}
737+
executor
738+
.spawn(async move {
739+
let mut cx = app.borrow_mut();
740+
cx.keyboard_layout = cx.platform.keyboard_layout();
741+
cx.keyboard_mapper = cx.platform.keyboard_mapper();
742+
cx.keyboard_layout_observers
743+
.clone()
744+
.retain(&(), move |callback| (callback)(&mut cx));
745+
})
746+
.detach();
744747
}
745748
}
746749
}));
747750

748751
platform.on_thermal_state_change(Box::new({
749752
let app = Rc::downgrade(&app);
753+
let executor = callback_executor;
750754
move || {
751755
if let Some(app) = app.upgrade() {
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-
}
756+
executor
757+
.spawn(async move {
758+
let mut cx = app.borrow_mut();
759+
cx.thermal_state_observers
760+
.clone()
761+
.retain(&(), move |callback| (callback)(&mut cx));
762+
})
763+
.detach();
759764
}
760765
}
761766
}));

0 commit comments

Comments
 (0)