From 2c74d75687598d20a7e328f0bccafe04ea75a620 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 8 May 2021 08:49:14 -0600 Subject: [PATCH] Fix double borrow error in Window::on_close callbacks when quitting app The simplest solution I could come up with was to make quitting the app asynchronous. Calling mac::Platform::quit enqueues a request to quit the app and then allows the call stack to fully return. This ensures we aren't holding a borrow when we quit and invoke all the Window::on_close callbacks. Seems like it should be fine to be async on quitting. --- gpui/src/platform/mac/platform.rs | 13 +++++++++++++ gpui/src/platform/mac/window.rs | 15 +++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index 29f75ed3ac..e7ccd280b4 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -347,7 +347,20 @@ impl platform::Platform for MacPlatform { } fn quit(&self) { + // Quitting the app causes us to close windows, which invokes `Window::on_close` callbacks + // synchronously before this method terminates. If we call `Platform::quit` while holding a + // borrow of the app state (which most of the time we will do), we will end up + // double-borrowing the app state in the `on_close` callbacks for our open windows. To solve + // this, we make quitting the application asynchronous so that we aren't holding borrows to + // the app state on the stack when we actually terminate the app. + + use super::dispatcher::{dispatch_async_f, dispatch_get_main_queue}; + unsafe { + dispatch_async_f(dispatch_get_main_queue(), ptr::null_mut(), Some(quit)); + } + + unsafe extern "C" fn quit(_: *mut c_void) { let app = NSApplication::sharedApplication(nil); let _: () = msg_send![app, terminate: nil]; } diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs index 92d0503aeb..2475a59381 100644 --- a/gpui/src/platform/mac/window.rs +++ b/gpui/src/platform/mac/window.rs @@ -380,12 +380,15 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) { extern "C" fn close_window(this: &Object, _: Sel) { unsafe { - let window_state = get_window_state(this); - let close_callback = window_state - .as_ref() - .try_borrow_mut() - .ok() - .and_then(|mut window_state| window_state.close_callback.take()); + let close_callback = { + let window_state = get_window_state(this); + window_state + .as_ref() + .try_borrow_mut() + .ok() + .and_then(|mut window_state| window_state.close_callback.take()) + }; + if let Some(callback) = close_callback { callback(); }