From 5d0bf9904edd278c2b5928fcdfc6c65bc7257a2f Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Fri, 6 Sep 2024 17:50:39 +0200 Subject: [PATCH] debug project leak stuff --- crates/assistant/src/assistant_panel.rs | 2 +- crates/editor/src/items.rs | 18 ++++++- crates/gpui/Cargo.toml | 3 +- crates/gpui/src/app.rs | 1 + crates/gpui/src/app/entity_map.rs | 31 +++++------ crates/gpui/src/window.rs | 1 + crates/project/src/lsp_store.rs | 15 ++++-- crates/project/src/project.rs | 6 +++ crates/session/src/session.rs | 16 +++--- crates/terminal_view/src/terminal_panel.rs | 2 +- crates/workspace/src/dock.rs | 6 ++- crates/workspace/src/pane.rs | 53 +++++++++++-------- crates/workspace/src/workspace.rs | 29 +++++++++-- crates/zed/src/zed.rs | 60 +++++++++++----------- 14 files changed, 153 insertions(+), 90 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 707daf03b1..0f0a03a810 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -341,7 +341,7 @@ impl AssistantPanel { let pane = cx.new_view(|cx| { let mut pane = Pane::new( workspace.weak_handle(), - workspace.project().clone(), + workspace.project().downgrade(), Default::default(), None, NewContext.boxed_clone(), diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 23293469dd..25ed3fa340 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1054,12 +1054,28 @@ impl SerializableItem for Editor { let buffer = buffer_task.await?; pane.update(&mut cx, |_, cx| { - cx.new_view(|cx| { + let editor = cx.new_view(|cx| { let mut editor = Editor::for_buffer(buffer, Some(project), cx); editor.read_scroll_position_from_db(item_id, workspace_id, cx); editor + }); + + let weak = editor.model.downgrade(); + cx.spawn(|_, cx| async move { + for i in 0..5 { + println!("{i}/5: going to sleep for 5 seconds"); + cx.background_executor() + .timer(std::time::Duration::from_secs(5)) + .await; + } + + println!("done sleeping"); + weak.assert_released(); }) + .detach(); + + editor }) }) } diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 4ce44ffce0..41b92633da 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -13,7 +13,6 @@ workspace = true [features] default = [] test-support = [ - "backtrace", "collections/test-support", "rand", "util/test-support", @@ -29,7 +28,7 @@ doctest = false [dependencies] anyhow.workspace = true async-task = "4.7" -backtrace = { version = "0.3", optional = true } +backtrace = { version = "0.3" } blade-graphics = { workspace = true, optional = true } blade-macros = { workspace = true, optional = true } blade-util = { workspace = true, optional = true } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index b726dabf77..c48133cdb6 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -765,6 +765,7 @@ impl AppContext { /// reference count has become zero. We invoke any release observers before dropping /// each entity. fn release_dropped_entities(&mut self) { + println!("releasing dropped entities"); loop { let dropped = self.entities.take_dropped(); if dropped.is_empty() { diff --git a/crates/gpui/src/app/entity_map.rs b/crates/gpui/src/app/entity_map.rs index e4da2a3010..70fc3a7a85 100644 --- a/crates/gpui/src/app/entity_map.rs +++ b/crates/gpui/src/app/entity_map.rs @@ -17,7 +17,7 @@ use std::{ thread::panicking, }; -#[cfg(any(test, feature = "test-support"))] +// #[cfg(any(test, feature = "test-support"))] use collections::HashMap; slotmap::new_key_type! { @@ -57,7 +57,7 @@ pub(crate) struct EntityMap { struct EntityRefCounts { counts: SlotMap, dropped_entity_ids: Vec, - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] leak_detector: LeakDetector, } @@ -68,7 +68,7 @@ impl EntityMap { ref_counts: Arc::new(RwLock::new(EntityRefCounts { counts: SlotMap::with_key(), dropped_entity_ids: Vec::new(), - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] leak_detector: LeakDetector { next_handle_id: 0, entity_handles: HashMap::default(), @@ -193,7 +193,7 @@ pub struct AnyModel { pub(crate) entity_id: EntityId, pub(crate) entity_type: TypeId, entity_map: Weak>, - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] handle_id: HandleId, } @@ -203,7 +203,7 @@ impl AnyModel { entity_id: id, entity_type, entity_map: entity_map.clone(), - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] handle_id: entity_map .upgrade() .unwrap() @@ -262,7 +262,7 @@ impl Clone for AnyModel { entity_id: self.entity_id, entity_type: self.entity_type, entity_map: self.entity_map.clone(), - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] handle_id: self .entity_map .upgrade() @@ -291,8 +291,9 @@ impl Drop for AnyModel { } } - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] if let Some(entity_map) = self.entity_map.upgrade() { + println!("dropped model"); entity_map .write() .leak_detector @@ -504,7 +505,7 @@ impl AnyWeakModel { entity_id: self.entity_id, entity_type: self.entity_type, entity_map: self.entity_ref_counts.clone(), - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] handle_id: self .entity_ref_counts .upgrade() @@ -516,7 +517,7 @@ impl AnyWeakModel { } /// Assert that model referenced by this weak handle has been released. - #[cfg(any(test, feature = "test-support"))] + // #[cfg(any(test, feature = "test-support"))] pub fn assert_released(&self) { self.entity_ref_counts .upgrade() @@ -641,23 +642,23 @@ impl PartialEq> for WeakModel { } } -#[cfg(any(test, feature = "test-support"))] +// #[cfg(any(test, feature = "test-support"))] static LEAK_BACKTRACE: std::sync::LazyLock = std::sync::LazyLock::new(|| std::env::var("LEAK_BACKTRACE").map_or(false, |b| !b.is_empty())); -#[cfg(any(test, feature = "test-support"))] +// #[cfg(any(test, feature = "test-support"))] #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] pub(crate) struct HandleId { id: u64, // id of the handle itself, not the pointed at object } -#[cfg(any(test, feature = "test-support"))] +// #[cfg(any(test, feature = "test-support"))] pub(crate) struct LeakDetector { next_handle_id: u64, entity_handles: HashMap>>, } -#[cfg(any(test, feature = "test-support"))] +// #[cfg(any(test, feature = "test-support"))] impl LeakDetector { #[track_caller] pub fn handle_created(&mut self, entity_id: EntityId) -> HandleId { @@ -666,7 +667,7 @@ impl LeakDetector { let handles = self.entity_handles.entry(entity_id).or_default(); handles.insert( handle_id, - LEAK_BACKTRACE.then(backtrace::Backtrace::new_unresolved), + LEAK_BACKTRACE.then(|| backtrace::Backtrace::new_unresolved()), ); handle_id } @@ -679,7 +680,7 @@ impl LeakDetector { pub fn assert_released(&mut self, entity_id: EntityId) { let handles = self.entity_handles.entry(entity_id).or_default(); if !handles.is_empty() { - for backtrace in handles.values_mut() { + for (_, backtrace) in handles { if let Some(mut backtrace) = backtrace.take() { backtrace.resolve(); eprintln!("Leaked handle: {:#?}", backtrace); diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 7d7ddc32e4..3b2ca4ac62 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -926,6 +926,7 @@ impl<'a> WindowContext<'a> { /// Close this window. pub fn remove_window(&mut self) { + println!("remove window!"); self.window.removed = true; } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 26ebb18456..999cc7cf57 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -254,7 +254,12 @@ impl LspStore { yarn, _maintain_workspace_config: Self::maintain_workspace_config(cx), _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx), - _subscription: cx.on_app_quit(Self::shutdown_language_servers), + _subscription: cx.on_release(|this, cx| { + println!("lsp store released"); + cx.background_executor() + .spawn(this.shutdown_language_servers()) + .detach(); + }), } } @@ -494,10 +499,8 @@ impl LspStore { self.active_entry = active_entry; } - fn shutdown_language_servers( - &mut self, - _cx: &mut ModelContext, - ) -> impl Future { + fn shutdown_language_servers(&mut self) -> impl Future { + println!("shutdown language servers"); let shutdown_futures = self .language_servers .drain() @@ -511,7 +514,9 @@ impl LspStore { .collect::>(); async move { + let count = shutdown_futures.len(); futures::future::join_all(shutdown_futures).await; + println!("done shutting down {} servers", count); } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index d17cb99fa7..57602e46fb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -658,6 +658,11 @@ impl Project { }); cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach(); + cx.on_release(|_, _| { + println!("project. on release"); + }) + .detach(); + Self { buffer_ordered_messages_tx: tx, collaborators: Default::default(), @@ -1717,6 +1722,7 @@ impl Project { } pub fn close(&mut self, cx: &mut ModelContext) { + println!("Closing the project"); cx.emit(Event::Closed); } diff --git a/crates/session/src/session.rs b/crates/session/src/session.rs index 01646b6f7a..6281d86865 100644 --- a/crates/session/src/session.rs +++ b/crates/session/src/session.rs @@ -68,15 +68,15 @@ impl AppSession { let _subscriptions = vec![cx.on_app_quit(Self::app_will_quit)]; let _serialization_task = Some(cx.spawn(|_, cx| async move { - loop { - if let Some(windows) = cx.update(|cx| cx.window_stack()).ok().flatten() { - store_window_stack(windows).await; - } + // loop { + // if let Some(windows) = cx.update(|cx| cx.window_stack()).ok().flatten() { + // store_window_stack(windows).await; + // } - cx.background_executor() - .timer(Duration::from_millis(100)) - .await; - } + // cx.background_executor() + // .timer(Duration::from_millis(100)) + // .await; + // } })); Self { diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 1e8d09f0a6..73d6a3283e 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -78,7 +78,7 @@ impl TerminalPanel { let pane = cx.new_view(|cx| { let mut pane = Pane::new( workspace.weak_handle(), - workspace.project().clone(), + workspace.project().downgrade(), Default::default(), None, NewTerminal.boxed_clone(), diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 28c462fbfc..e1e3e635fa 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -368,9 +368,13 @@ impl Dock { cx.observe(&panel, |_, _, cx| cx.notify()), cx.observe_global::({ let workspace = workspace.clone(); - let panel = panel.clone(); + let panel = panel.downgrade(); move |this, cx| { + let Some(panel) = panel.upgrade() else { + return; + }; + let new_position = panel.read(cx).position(cx); if new_position == this.position { return; diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 51422bddf4..24aa204cd3 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -18,8 +18,8 @@ use gpui::{ AppContext, AsyncWindowContext, ClickEvent, ClipboardItem, Div, DragMoveEvent, EntityId, EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, FocusableView, KeyContext, Model, MouseButton, MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, - ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakView, - WindowContext, + ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakModel, + WeakView, WindowContext, }; use itertools::Itertools; use parking_lot::Mutex; @@ -255,7 +255,7 @@ pub struct Pane { nav_history: NavHistory, toolbar: View, pub(crate) workspace: WeakView, - project: Model, + project: WeakModel, drag_split_direction: Option, can_drop_predicate: Option bool>>, custom_drop_handle: @@ -337,7 +337,7 @@ impl EventEmitter for Pane {} impl Pane { pub fn new( workspace: WeakView, - project: Model, + project: WeakModel, next_timestamp: Arc, can_drop_predicate: Option bool + 'static>>, double_click_dispatch_action: Box, @@ -802,14 +802,15 @@ impl Pane { ) { if item.is_singleton(cx) { if let Some(&entry_id) = item.project_entry_ids(cx).first() { - let project = self.project.read(cx); - if let Some(project_path) = project.path_for_entry(entry_id, cx) { - let abs_path = project.absolute_path(&project_path, cx); - self.nav_history - .0 - .lock() - .paths_by_item - .insert(item.item_id(), (project_path, abs_path)); + if let Some(project) = self.project.upgrade().map(|project| project.read(cx)) { + if let Some(project_path) = project.path_for_entry(entry_id, cx) { + let abs_path = project.absolute_path(&project_path, cx); + self.nav_history + .0 + .lock() + .paths_by_item + .insert(item.item_id(), (project_path, abs_path)); + } } } } @@ -1811,7 +1812,7 @@ impl Pane { let icon_color = if ItemSettings::get_global(cx).git_status { project_path .as_ref() - .and_then(|path| self.project.read(cx).entry_for_path(path, cx)) + .and_then(|path| self.project.upgrade()?.read(cx).entry_for_path(path, cx)) .map(|entry| { Self::git_aware_icon_color(entry.git_status, entry.is_ignored, is_active) }) @@ -2057,11 +2058,13 @@ impl Pane { entry_id: Some(entry_id), })), cx.handler_for(&pane, move |pane, cx| { - pane.project.update(cx, |_, cx| { - cx.emit(project::Event::RevealInProjectPanel( - ProjectEntryId::from_proto(entry_id), - )) - }); + pane.project + .update(cx, |_, cx| { + cx.emit(project::Event::RevealInProjectPanel( + ProjectEntryId::from_proto(entry_id), + )) + }) + .log_err(); }), ) .when_some(parent_abs_path, |menu, parent_abs_path| { @@ -2605,9 +2608,11 @@ impl Render for Pane { .map(ProjectEntryId::from_proto) .or_else(|| pane.active_item()?.project_entry_ids(cx).first().copied()); if let Some(entry_id) = entry_id { - pane.project.update(cx, |_, cx| { - cx.emit(project::Event::RevealInProjectPanel(entry_id)) - }); + pane.project + .update(cx, |_, cx| { + cx.emit(project::Event::RevealInProjectPanel(entry_id)) + }) + .log_err(); } }), ) @@ -2615,7 +2620,11 @@ impl Render for Pane { pane.child(self.render_tab_bar(cx)) }) .child({ - let has_worktrees = self.project.read(cx).worktrees(cx).next().is_some(); + let has_worktrees = self + .project + .upgrade() + .and_then(|project| project.read(cx).worktrees(cx).next()) + .is_some(); // main content div() .flex_1() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 219f75624c..2ca65b1672 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -873,7 +873,7 @@ impl Workspace { let center_pane = cx.new_view(|cx| { Pane::new( weak_handle.clone(), - project.clone(), + project.downgrade(), pane_history_timestamp.clone(), None, NewFile.boxed_clone(), @@ -1004,6 +1004,7 @@ impl Workspace { cx.on_release(|this, window, cx| { this.app_state.workspace_store.update(cx, |store, _| { let window = window.downcast::().unwrap(); + println!("workspace released!"); store.workspaces.remove(&window); }) }), @@ -1634,17 +1635,31 @@ impl Workspace { } pub fn close_window(&mut self, _: &CloseWindow, cx: &mut ViewContext) { + // let project = self.project.downgrade(); + // let handle = cx.model(); + // let weak = self.weak_handle(); + let prepare = self.prepare_to_close(CloseIntent::CloseWindow, cx); let window = cx.window_handle(); cx.spawn(|_, mut cx| async move { if prepare.await? { window.update(&mut cx, |_, cx| { + println!("remove window"); cx.remove_window(); })?; + + // cx.spawn(|cx| async move { + // println!("going to sleep for 8 sec"); + // cx.background_executor().timer(Duration::from_secs(8)).await; + // println!("slept for 8 sec"); + // println!("asserting the project weak handle is released"); + // project.assert_released(); + // }) + // .detach(); } anyhow::Ok(()) }) - .detach_and_log_err(cx) + .detach_and_log_err(cx); } pub fn prepare_to_close( @@ -2408,7 +2423,7 @@ impl Workspace { let pane = cx.new_view(|cx| { Pane::new( self.weak_handle(), - self.project.clone(), + self.project.downgrade(), self.pane_history_timestamp.clone(), None, NewFile.boxed_clone(), @@ -4020,6 +4035,7 @@ impl Workspace { } fn serialize_workspace_internal(&self, cx: &mut WindowContext) -> Task<()> { + println!("--- serialize workspace internal start ---"); let Some(database_id) = self.database_id() else { return Task::ready(()); }; @@ -4161,7 +4177,10 @@ impl Workspace { session_id: self.session_id.clone(), window_id: Some(cx.window_handle().window_id().as_u64()), }; - return cx.spawn(|_| persistence::DB.save_workspace(serialized_workspace)); + return cx.spawn(|_| async move { + persistence::DB.save_workspace(serialized_workspace).await; + println!("--- serialize workspace internal END ---") + }); } Task::ready(()) } @@ -4177,6 +4196,7 @@ impl Workspace { let mut serializable_items = items_rx.ready_chunks(CHUNK_SIZE); while let Some(items_received) = serializable_items.next().await { + println!("serializing item!"); let unique_items = items_received .into_iter() @@ -4199,6 +4219,7 @@ impl Workspace { cx.background_executor().timer(THROTTLE_TIME).await; } + println!("done serializing items"); Ok(()) } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 74bdfb666b..a769a2e9b1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -251,47 +251,47 @@ pub fn initialize_workspace( let prompt_builder = prompt_builder.clone(); cx.spawn(|workspace_handle, mut cx| async move { - let assistant_panel = - assistant::AssistantPanel::load(workspace_handle.clone(), prompt_builder, cx.clone()); + // // let assistant_panel = + // // assistant::AssistantPanel::load(workspace_handle.clone(), prompt_builder, cx.clone()); - let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); + // // let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); let outline_panel = OutlinePanel::load(workspace_handle.clone(), cx.clone()); - let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone()); - let channels_panel = - collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone()); - let chat_panel = - collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone()); - let notification_panel = collab_ui::notification_panel::NotificationPanel::load( - workspace_handle.clone(), - cx.clone(), - ); + // let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone()); + // // let channels_panel = + // // collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone()); + // // let chat_panel = + // // collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone()); + // let notification_panel = collab_ui::notification_panel::NotificationPanel::load( + // workspace_handle.clone(), + // cx.clone(), + // ); let ( - project_panel, + // project_panel, outline_panel, - terminal_panel, - assistant_panel, - channels_panel, - chat_panel, - notification_panel, + // terminal_panel, + // assistant_panel, + // channels_panel, + // chat_panel, + // notification_panel, ) = futures::try_join!( - project_panel, + // project_panel, outline_panel, - terminal_panel, - assistant_panel, - channels_panel, - chat_panel, - notification_panel, + // terminal_panel, + // assistant_panel, + // channels_panel, + // chat_panel, + // notification_panel, )?; workspace_handle.update(&mut cx, |workspace, cx| { - workspace.add_panel(assistant_panel, cx); - workspace.add_panel(project_panel, cx); + // workspace.add_panel(assistant_panel, cx); + // workspace.add_panel(project_panel, cx); workspace.add_panel(outline_panel, cx); - workspace.add_panel(terminal_panel, cx); - workspace.add_panel(channels_panel, cx); - workspace.add_panel(chat_panel, cx); - workspace.add_panel(notification_panel, cx); + // workspace.add_panel(terminal_panel, cx); + // workspace.add_panel(channels_panel, cx); + // workspace.add_panel(chat_panel, cx); + // workspace.add_panel(notification_panel, cx); cx.focus_self(); }) })