gpui: Fix thermal state notifications on intel macOS (#49086) (cherry-pick to preview) (#49248)

zed-zippy[bot] and Marco Mihai Condrache created

Cherry-pick of #49086 to preview

----
Fixes
https://github.com/zed-industries/zed/pull/45638#issuecomment-3893732034
Closes #49005

The issue was similar to the one we already had with the quit method

https://github.com/zed-industries/zed/blob/8249ef56187b966c33f6667d0d3a35d88d8f2dc0/crates/gpui/src/platform/mac/platform.rs#L491

@notpeter Could you please test this branch to confirm it resolves the
issue?

- [ ] Tests or screenshots needed?
- [x] Code Reviewed
- [ ] Manual QA

Release Notes:

- Fixed an issue where Zed would randomly crash on macOS intel

Co-authored-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>

Change summary

crates/gpui/src/platform/mac/platform.rs | 35 +++++++++++++++++++------
1 file changed, 26 insertions(+), 9 deletions(-)

Detailed changes

crates/gpui/src/platform/mac/platform.rs 🔗

@@ -1258,16 +1258,33 @@ extern "C" fn on_keyboard_layout_change(this: &mut Object, _: Sel, _: id) {
 }
 
 extern "C" fn on_thermal_state_change(this: &mut Object, _: Sel, _: id) {
+    // Defer to the next run loop iteration to avoid re-entrant borrows of the App RefCell,
+    // as NSNotificationCenter delivers this notification synchronously and it may fire while
+    // the App is already borrowed (same pattern as quit() above).
+    use super::dispatcher::{dispatch_get_main_queue, dispatch_sys::dispatch_async_f};
+
     let platform = unsafe { get_mac_platform(this) };
-    let mut lock = platform.0.lock();
-    if let Some(mut callback) = lock.on_thermal_state_change.take() {
-        drop(lock);
-        callback();
-        platform
-            .0
-            .lock()
-            .on_thermal_state_change
-            .get_or_insert(callback);
+    let platform_ptr = platform as *const MacPlatform as *mut c_void;
+    unsafe {
+        dispatch_async_f(
+            dispatch_get_main_queue(),
+            platform_ptr,
+            Some(on_thermal_state_change),
+        );
+    }
+
+    unsafe extern "C" fn on_thermal_state_change(context: *mut c_void) {
+        let platform = unsafe { &*(context as *const MacPlatform) };
+        let mut lock = platform.0.lock();
+        if let Some(mut callback) = lock.on_thermal_state_change.take() {
+            drop(lock);
+            callback();
+            platform
+                .0
+                .lock()
+                .on_thermal_state_change
+                .get_or_insert(callback);
+        }
     }
 }