Detailed changes
@@ -42,7 +42,7 @@ fn run_example() {
let mut bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
cx.bind_keys([KeyBinding::new("cmd-w", CloseWindow, None)]);
- cx.on_window_closed(|cx| {
+ cx.on_window_closed(|cx, _window_id| {
if cx.windows().is_empty() {
cx.quit();
}
@@ -457,7 +457,7 @@ fn run_example() {
|window, cx| cx.new(|cx| PaintingViewer::new(window, cx)),
)
.unwrap();
- cx.on_window_closed(|cx| {
+ cx.on_window_closed(|cx, _window_id| {
cx.quit();
})
.detach();
@@ -241,7 +241,7 @@ type Listener = Box<dyn FnMut(&dyn Any, &mut App) -> bool + 'static>;
pub(crate) type KeystrokeObserver =
Box<dyn FnMut(&KeystrokeEvent, &mut Window, &mut App) -> bool + 'static>;
type QuitHandler = Box<dyn FnOnce(&mut App) -> LocalBoxFuture<'static, ()> + 'static>;
-type WindowClosedHandler = Box<dyn FnMut(&mut App)>;
+type WindowClosedHandler = Box<dyn FnMut(&mut App, WindowId)>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut App) + 'static>;
type NewEntityListener = Box<dyn FnMut(AnyEntity, &mut Option<&mut Window>, &mut App) + 'static>;
@@ -1567,7 +1567,7 @@ impl App {
cx.windows.remove(id);
cx.window_closed_observers.clone().retain(&(), |callback| {
- callback(cx);
+ callback(cx, id);
true
});
@@ -2041,7 +2041,10 @@ impl App {
/// Register a callback to be invoked when a window is closed
/// The window is no longer accessible at the point this callback is invoked.
- pub fn on_window_closed(&self, mut on_closed: impl FnMut(&mut App) + 'static) -> Subscription {
+ pub fn on_window_closed(
+ &self,
+ mut on_closed: impl FnMut(&mut App, WindowId) + 'static,
+ ) -> Subscription {
let (subscription, activate) = self.window_closed_observers.insert((), Box::new(on_closed));
activate();
subscription
@@ -2357,13 +2360,12 @@ impl AppContext for App {
let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
cx.push_effect(Effect::EntityCreated {
- entity: handle.clone().into_any(),
+ entity: handle.into_any(),
tid: TypeId::of::<T>(),
window: cx.window_update_stack.last().cloned(),
});
- cx.entities.insert(slot, entity);
- handle
+ cx.entities.insert(slot, entity)
})
}
@@ -1016,7 +1016,7 @@ impl LeakDetector {
.unwrap();
}
}
- panic!("{out}");
+ panic!("Handles for {} leaked:\n{out}", data.type_name);
}
}
@@ -1516,7 +1516,7 @@ impl SettingsWindow {
})
.detach();
- cx.on_window_closed(|cx| {
+ cx.on_window_closed(|cx, _window_id| {
if let Some(existing_window) = cx
.windows()
.into_iter()
@@ -13,6 +13,8 @@ workspace = true
[features]
tracy = ["ztracing/tracy"]
+# LEAK_BACKTRACE=1 cargo run --features zed/track-project-leak --profile release-fast
+track-project-leak = ["gpui/leak-detection"]
test-support = [
"gpui/test-support",
"gpui_platform/screen-capture",
@@ -22,7 +22,11 @@ use crate::STARTUP_TIME;
const MAX_HANG_TRACES: usize = 3;
pub fn init(client: Arc<Client>, cx: &mut App) {
- monitor_hangs(cx);
+ if cfg!(debug_assertions) {
+ log::info!("Debug assertions enabled, skipping hang monitoring");
+ } else {
+ monitor_hangs(cx);
+ }
cx.on_flags_ready({
let client = client.clone();
@@ -291,7 +291,7 @@ fn bind_on_window_closed(cx: &mut App) -> Option<gpui::Subscription> {
.on_last_window_closed
.is_quit_app()
.then(|| {
- cx.on_window_closed(|cx| {
+ cx.on_window_closed(|cx, _window_id| {
if cx.windows().is_empty() {
cx.quit();
}
@@ -300,7 +300,7 @@ fn bind_on_window_closed(cx: &mut App) -> Option<gpui::Subscription> {
}
#[cfg(not(target_os = "macos"))]
{
- Some(cx.on_window_closed(|cx| {
+ Some(cx.on_window_closed(|cx, _window_id| {
if cx.windows().is_empty() {
cx.quit();
}
@@ -372,6 +372,33 @@ pub fn initialize_workspace(
return;
};
+ #[cfg(feature = "track-project-leak")]
+ {
+ let multi_workspace_handle = cx.weak_entity();
+ let workspace_handle = _multi_workspace.workspace().downgrade();
+ let project_handle = _multi_workspace.workspace().read(cx).project().downgrade();
+ let window_id_2 = window.window_handle().window_id();
+ cx.on_window_closed(move |cx, window_id| {
+ let multi_workspace_handle = multi_workspace_handle.clone();
+ let workspace_handle = workspace_handle.clone();
+ let project_handle = project_handle.clone();
+ if window_id != window_id_2 {
+ return;
+ }
+ cx.spawn(async move |cx| {
+ cx.background_executor()
+ .timer(Duration::from_millis(501))
+ .await;
+
+ multi_workspace_handle.assert_released();
+ workspace_handle.assert_released();
+ project_handle.assert_released();
+ })
+ .detach();
+ })
+ .detach();
+ }
+
let multi_workspace_handle = cx.entity().downgrade();
window.on_window_should_close(cx, move |window, cx| {
multi_workspace_handle