gpui: Fix `update_window` to `borrow_mut` will crash on Windows (#24545)

Jason Lee and Sunli created

Release Notes:

- N/A

---


When we use `window_handle` to draw WebView on Windows, this will crash
by:

This error caused by when used WebView2.

```
thread 'main' panicked at crates\gpui\src\app\async_context.rs:91:28:
already borrowed: BorrowMutError
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at library\core\src\panicking.rs:221:5:
panic in a function that cannot unwind
```

Try this https://github.com/tauri-apps/wry/pull/1383 on Windows can
replay the crash.

In fact, we had done [a similar fix around August last
year](https://github.com/huacnlee/zed/pull/6), but we used the unsafe
method to avoid crashes in that version, we felt that it was not a good
change, so we do not make PR.

Today @sunli829 thought about it again and changed the method. Now using
`try_borrow_mut` is similar to the previous `borrow_mut`.


https://github.com/zed-industries/zed/blob/691de6b4b36d3ada91a1e238904b065eec454188/crates/gpui/src/app.rs#L70-L78

I have tested to start Zed by those changes, it is looks no problem.

Co-authored-by: Sunli <scott_s829@163.com>

Change summary

crates/gpui/src/app.rs               | 12 +++++++++++-
crates/gpui/src/app/async_context.rs |  2 +-
2 files changed, 12 insertions(+), 2 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -1,6 +1,6 @@
 use std::{
     any::{TypeId, type_name},
-    cell::{Ref, RefCell, RefMut},
+    cell::{BorrowMutError, Ref, RefCell, RefMut},
     marker::PhantomData,
     mem,
     ops::{Deref, DerefMut},
@@ -79,6 +79,16 @@ impl AppCell {
         }
         AppRefMut(self.app.borrow_mut())
     }
+
+    #[doc(hidden)]
+    #[track_caller]
+    pub fn try_borrow_mut(&self) -> Result<AppRefMut, BorrowMutError> {
+        if option_env!("TRACK_THREAD_BORROWS").is_some() {
+            let thread_id = std::thread::current().id();
+            eprintln!("borrowed {thread_id:?}");
+        }
+        Ok(AppRefMut(self.app.try_borrow_mut()?))
+    }
 }
 
 #[doc(hidden)]

crates/gpui/src/app/async_context.rs 🔗

@@ -88,7 +88,7 @@ impl AppContext for AsyncApp {
         F: FnOnce(AnyView, &mut Window, &mut App) -> T,
     {
         let app = self.app.upgrade().context("app was released")?;
-        let mut lock = app.borrow_mut();
+        let mut lock = app.try_borrow_mut()?;
         lock.update_window(window, f)
     }