Don't allow strong view handles to be read/updated with an AsyncAppContext

This avoids an invitation to hold strong view handles across async await
points, which is a common source of leaks.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2023-04-26 13:36:13 +02:00
parent 689e878bd8
commit 6317e885c7
14 changed files with 129 additions and 163 deletions

View file

@ -61,7 +61,7 @@ fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut AppCont
}); });
let workspace = if let Some(existing_workspace) = existing_workspace { let workspace = if let Some(existing_workspace) = existing_workspace {
existing_workspace existing_workspace.downgrade()
} else { } else {
let active_call = cx.read(ActiveCall::global); let active_call = cx.read(ActiveCall::global);
let room = active_call let room = active_call
@ -93,7 +93,7 @@ fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut AppCont
workspace workspace
}, },
); );
workspace workspace.downgrade()
}; };
cx.activate_window(workspace.window_id()); cx.activate_window(workspace.window_id());

View file

@ -2715,14 +2715,15 @@ impl Editor {
let apply_code_actions = workspace.project().clone().update(cx, |project, cx| { let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
project.apply_code_action(buffer, action, true, cx) project.apply_code_action(buffer, action, true, cx)
}); });
let editor = editor.downgrade();
Some(cx.spawn(|workspace, cx| async move { Some(cx.spawn(|workspace, cx| async move {
let project_transaction = apply_code_actions.await?; let project_transaction = apply_code_actions.await?;
Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
})) }))
} }
async fn open_project_transaction( async fn open_project_transaction(
this: ViewHandle<Editor>, this: &WeakViewHandle<Editor>,
workspace: WeakViewHandle<Workspace>, workspace: WeakViewHandle<Workspace>,
transaction: ProjectTransaction, transaction: ProjectTransaction,
title: String, title: String,
@ -5943,10 +5944,11 @@ impl Editor {
project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx) project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
}); });
let editor = editor.downgrade();
Some(cx.spawn(|workspace, mut cx| async move { Some(cx.spawn(|workspace, mut cx| async move {
let project_transaction = rename.await?; let project_transaction = rename.await?;
Self::open_project_transaction( Self::open_project_transaction(
editor.clone(), &editor,
workspace, workspace,
project_transaction, project_transaction,
format!("Rename: {}{}", old_name, new_name), format!("Rename: {}{}", old_name, new_name),
@ -6761,7 +6763,8 @@ impl Editor {
let editor = editor let editor = editor
.await? .await?
.downcast::<Editor>() .downcast::<Editor>()
.ok_or_else(|| anyhow!("opened item was not an editor"))?; .ok_or_else(|| anyhow!("opened item was not an editor"))?
.downgrade();
editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
let buffer = editor let buffer = editor
.buffer() .buffer()
@ -6783,6 +6786,7 @@ impl Editor {
anyhow::Ok(()) anyhow::Ok(())
})??; })??;
anyhow::Ok(()) anyhow::Ok(())
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);

View file

@ -67,6 +67,7 @@ impl FollowableItem for Editor {
.collect::<Vec<_>>() .collect::<Vec<_>>()
}); });
let pane = pane.downgrade();
Some(cx.spawn(|mut cx| async move { Some(cx.spawn(|mut cx| async move {
let mut buffers = futures::future::try_join_all(buffers).await?; let mut buffers = futures::future::try_join_all(buffers).await?;
let editor = pane.read_with(&cx, |pane, cx| { let editor = pane.read_with(&cx, |pane, cx| {
@ -127,7 +128,7 @@ impl FollowableItem for Editor {
}; };
update_editor_from_message( update_editor_from_message(
editor.clone(), editor.downgrade(),
project, project,
proto::update_view::Editor { proto::update_view::Editor {
selections: state.selections, selections: state.selections,
@ -286,9 +287,6 @@ impl FollowableItem for Editor {
let update_view::Variant::Editor(message) = message; let update_view::Variant::Editor(message) = message;
let project = project.clone(); let project = project.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let this = this
.upgrade(&cx)
.ok_or_else(|| anyhow!("editor was dropped"))?;
update_editor_from_message(this, project, message, &mut cx).await update_editor_from_message(this, project, message, &mut cx).await
}) })
} }
@ -304,7 +302,7 @@ impl FollowableItem for Editor {
} }
async fn update_editor_from_message( async fn update_editor_from_message(
this: ViewHandle<Editor>, this: WeakViewHandle<Editor>,
project: ModelHandle<Project>, project: ModelHandle<Project>,
message: proto::update_view::Editor, message: proto::update_view::Editor,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,

View file

@ -124,11 +124,10 @@ impl FeedbackEditor {
&["Yes, Submit!", "No"], &["Yes, Submit!", "No"],
); );
let this = cx.handle();
let client = cx.global::<Arc<Client>>().clone(); let client = cx.global::<Arc<Client>>().clone();
let specs = self.system_specs.clone(); let specs = self.system_specs.clone();
cx.spawn(|_, mut cx| async move { cx.spawn(|this, mut cx| async move {
let answer = answer.recv().await; let answer = answer.recv().await;
if answer == Some(0) { if answer == Some(0) {

View file

@ -127,18 +127,8 @@ pub trait BorrowAppContext {
} }
pub trait BorrowWindowContext { pub trait BorrowWindowContext {
type ReturnValue<T>; fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T;
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(
&self,
window_id: usize,
f: F,
) -> Self::ReturnValue<T>;
fn update<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
f: F,
) -> Self::ReturnValue<T>;
} }
#[derive(Clone)] #[derive(Clone)]
@ -381,28 +371,6 @@ impl BorrowAppContext for AsyncAppContext {
} }
} }
impl BorrowWindowContext for AsyncAppContext {
type ReturnValue<T> = Result<T>;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> Result<T> {
self.0
.borrow()
.read_window(window_id, f)
.ok_or_else(|| anyhow!("window was closed"))
}
fn update<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
f: F,
) -> Result<T> {
self.0
.borrow_mut()
.update_window(window_id, f)
.ok_or_else(|| anyhow!("window was closed"))
}
}
type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize); type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize);
type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext); type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
@ -3330,8 +3298,6 @@ impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
} }
impl<V> BorrowWindowContext for ViewContext<'_, '_, V> { impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
type ReturnValue<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
BorrowWindowContext::read_with(&*self.window_context, window_id, f) BorrowWindowContext::read_with(&*self.window_context, window_id, f)
} }
@ -3384,8 +3350,6 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
} }
impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> { impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
type ReturnValue<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
BorrowWindowContext::read_with(&*self.view_context, window_id, f) BorrowWindowContext::read_with(&*self.view_context, window_id, f)
} }
@ -3722,7 +3686,7 @@ impl<T: View> ViewHandle<T> {
cx.read_view(self) cx.read_view(self)
} }
pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::ReturnValue<S> pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> S
where where
C: BorrowWindowContext, C: BorrowWindowContext,
F: FnOnce(&T, &ViewContext<T>) -> S, F: FnOnce(&T, &ViewContext<T>) -> S,
@ -3733,7 +3697,7 @@ impl<T: View> ViewHandle<T> {
}) })
} }
pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::ReturnValue<S> pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> S
where where
C: BorrowWindowContext, C: BorrowWindowContext,
F: FnOnce(&mut T, &mut ViewContext<T>) -> S, F: FnOnce(&mut T, &mut ViewContext<T>) -> S,

View file

@ -390,8 +390,6 @@ impl BorrowAppContext for TestAppContext {
} }
impl BorrowWindowContext for TestAppContext { impl BorrowWindowContext for TestAppContext {
type ReturnValue<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
self.cx self.cx
.borrow() .borrow()

View file

@ -142,8 +142,6 @@ impl BorrowAppContext for WindowContext<'_> {
} }
impl BorrowWindowContext for WindowContext<'_> { impl BorrowWindowContext for WindowContext<'_> {
type ReturnValue<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
if self.window_id == window_id { if self.window_id == window_id {
f(self) f(self)

View file

@ -56,7 +56,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut AppContext) {
.await; .await;
if let Some(Some(Ok(item))) = opened.first() { if let Some(Some(Ok(item))) = opened.first() {
if let Some(editor) = item.downcast::<Editor>() { if let Some(editor) = item.downcast::<Editor>().map(|editor| editor.downgrade()) {
editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
let len = editor.buffer().read(cx).len(cx); let len = editor.buffer().read(cx).len(cx);
editor.change_selections(Some(Autoscroll::center()), cx, |s| { editor.change_selections(Some(Autoscroll::center()), cx, |s| {

View file

@ -811,8 +811,6 @@ mod tests {
} }
impl BorrowWindowContext for DockTestContext<'_> { impl BorrowWindowContext for DockTestContext<'_> {
type ReturnValue<T> = T;
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T { fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
BorrowWindowContext::read_with(self.cx, window_id, f) BorrowWindowContext::read_with(self.cx, window_id, f)
} }

View file

@ -214,26 +214,10 @@ pub fn init(cx: &mut AppContext) {
Pane::reopen_closed_item(workspace, cx).detach(); Pane::reopen_closed_item(workspace, cx).detach();
}); });
cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| { cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| {
Pane::go_back( Pane::go_back(workspace, action.pane.clone(), cx).detach();
workspace,
action
.pane
.as_ref()
.and_then(|weak_handle| weak_handle.upgrade(cx)),
cx,
)
.detach();
}); });
cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| { cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| {
Pane::go_forward( Pane::go_forward(workspace, action.pane.clone(), cx).detach();
workspace,
action
.pane
.as_ref()
.and_then(|weak_handle| weak_handle.upgrade(cx)),
cx,
)
.detach();
}); });
} }
@ -392,12 +376,12 @@ impl Pane {
pub fn go_back( pub fn go_back(
workspace: &mut Workspace, workspace: &mut Workspace,
pane: Option<ViewHandle<Pane>>, pane: Option<WeakViewHandle<Pane>>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
Self::navigate_history( Self::navigate_history(
workspace, workspace,
pane.unwrap_or_else(|| workspace.active_pane().clone()), pane.unwrap_or_else(|| workspace.active_pane().downgrade()),
NavigationMode::GoingBack, NavigationMode::GoingBack,
cx, cx,
) )
@ -405,12 +389,12 @@ impl Pane {
pub fn go_forward( pub fn go_forward(
workspace: &mut Workspace, workspace: &mut Workspace,
pane: Option<ViewHandle<Pane>>, pane: Option<WeakViewHandle<Pane>>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
Self::navigate_history( Self::navigate_history(
workspace, workspace,
pane.unwrap_or_else(|| workspace.active_pane().clone()), pane.unwrap_or_else(|| workspace.active_pane().downgrade()),
NavigationMode::GoingForward, NavigationMode::GoingForward,
cx, cx,
) )
@ -422,7 +406,7 @@ impl Pane {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
Self::navigate_history( Self::navigate_history(
workspace, workspace,
workspace.active_pane().clone(), workspace.active_pane().downgrade(),
NavigationMode::ReopeningClosedItem, NavigationMode::ReopeningClosedItem,
cx, cx,
) )
@ -450,62 +434,62 @@ impl Pane {
fn navigate_history( fn navigate_history(
workspace: &mut Workspace, workspace: &mut Workspace,
pane: ViewHandle<Pane>, pane: WeakViewHandle<Pane>,
mode: NavigationMode, mode: NavigationMode,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
cx.focus(&pane); let to_load = if let Some(pane) = pane.upgrade(cx) {
cx.focus(&pane);
let to_load = pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
loop { loop {
// Retrieve the weak item handle from the history. // Retrieve the weak item handle from the history.
let entry = pane.nav_history.borrow_mut().pop(mode, cx)?; let entry = pane.nav_history.borrow_mut().pop(mode, cx)?;
// If the item is still present in this pane, then activate it. // If the item is still present in this pane, then activate it.
if let Some(index) = entry if let Some(index) = entry
.item .item
.upgrade(cx) .upgrade(cx)
.and_then(|v| pane.index_for_item(v.as_ref())) .and_then(|v| pane.index_for_item(v.as_ref()))
{ {
let prev_active_item_index = pane.active_item_index; let prev_active_item_index = pane.active_item_index;
pane.nav_history.borrow_mut().set_mode(mode); pane.nav_history.borrow_mut().set_mode(mode);
pane.activate_item(index, true, true, cx); pane.activate_item(index, true, true, cx);
pane.nav_history pane.nav_history
.borrow_mut() .borrow_mut()
.set_mode(NavigationMode::Normal); .set_mode(NavigationMode::Normal);
let mut navigated = prev_active_item_index != pane.active_item_index; let mut navigated = prev_active_item_index != pane.active_item_index;
if let Some(data) = entry.data { if let Some(data) = entry.data {
navigated |= pane.active_item()?.navigate(data, cx); navigated |= pane.active_item()?.navigate(data, cx);
}
if navigated {
break None;
}
} }
// If the item is no longer present in this pane, then retrieve its
if navigated { // project path in order to reopen it.
break None; else {
break pane
.nav_history
.borrow()
.paths_by_item
.get(&entry.item.id())
.cloned()
.map(|project_path| (project_path, entry));
} }
} }
// If the item is no longer present in this pane, then retrieve its })
// project path in order to reopen it. } else {
else { None
break pane };
.nav_history
.borrow()
.paths_by_item
.get(&entry.item.id())
.cloned()
.map(|project_path| (project_path, entry));
}
}
});
if let Some((project_path, entry)) = to_load { if let Some((project_path, entry)) = to_load {
// If the item was no longer present, then load it again from its previous path. // If the item was no longer present, then load it again from its previous path.
let pane = pane.downgrade();
let task = workspace.load_path(project_path, cx); let task = workspace.load_path(project_path, cx);
cx.spawn(|workspace, mut cx| async move { cx.spawn(|workspace, mut cx| async move {
let task = task.await; let task = task.await;
let pane = pane
.upgrade(&cx)
.ok_or_else(|| anyhow!("pane was dropped"))?;
let mut navigated = false; let mut navigated = false;
if let Some((project_entry_id, build_item)) = task.log_err() { if let Some((project_entry_id, build_item)) = task.log_err() {
let prev_active_item_id = pane.update(&mut cx, |pane, _| { let prev_active_item_id = pane.update(&mut cx, |pane, _| {
@ -514,15 +498,18 @@ impl Pane {
})?; })?;
let item = workspace.update(&mut cx, |workspace, cx| { let item = workspace.update(&mut cx, |workspace, cx| {
Self::open_item( let pane = pane
.upgrade(cx)
.ok_or_else(|| anyhow!("pane was dropped"))?;
anyhow::Ok(Self::open_item(
workspace, workspace,
pane.clone(), pane.clone(),
project_entry_id, project_entry_id,
true, true,
cx, cx,
build_item, build_item,
) ))
})?; })??;
pane.update(&mut cx, |pane, cx| { pane.update(&mut cx, |pane, cx| {
navigated |= Some(item.id()) != prev_active_item_id; navigated |= Some(item.id()) != prev_active_item_id;
@ -973,6 +960,7 @@ impl Pane {
// of what content they would be saving. // of what content they would be saving.
items_to_close.sort_by_key(|item| !item.is_singleton(cx)); items_to_close.sort_by_key(|item| !item.is_singleton(cx));
let pane = pane.downgrade();
cx.spawn(|workspace, mut cx| async move { cx.spawn(|workspace, mut cx| async move {
let mut saved_project_items_ids = HashSet::default(); let mut saved_project_items_ids = HashSet::default();
for item in items_to_close.clone() { for item in items_to_close.clone() {
@ -1084,7 +1072,7 @@ impl Pane {
pub async fn save_item( pub async fn save_item(
project: ModelHandle<Project>, project: ModelHandle<Project>,
pane: &ViewHandle<Pane>, pane: &WeakViewHandle<Pane>,
item_ix: usize, item_ix: usize,
item: &dyn ItemHandle, item: &dyn ItemHandle,
should_prompt_for_save: bool, should_prompt_for_save: bool,

View file

@ -1,28 +1,24 @@
use std::{ use crate::{
path::{Path, PathBuf}, dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
sync::Arc,
}; };
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use async_recursion::async_recursion; use async_recursion::async_recursion;
use gpui::{
platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle,
};
use db::sqlez::{ use db::sqlez::{
bindable::{Bind, Column, StaticColumnCount}, bindable::{Bind, Column, StaticColumnCount},
statement::Statement, statement::Statement,
}; };
use gpui::{
platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle,
};
use project::Project; use project::Project;
use settings::DockAnchor; use settings::DockAnchor;
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::ResultExt; use util::ResultExt;
use uuid::Uuid; use uuid::Uuid;
use crate::{
dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>); pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
@ -134,7 +130,7 @@ impl SerializedPaneGroup {
} }
SerializedPaneGroup::Pane(serialized_pane) => { SerializedPaneGroup::Pane(serialized_pane) => {
let pane = workspace let pane = workspace
.update(cx, |workspace, cx| workspace.add_pane(cx)) .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
.log_err()?; .log_err()?;
let active = serialized_pane.active; let active = serialized_pane.active;
serialized_pane serialized_pane
@ -146,8 +142,10 @@ impl SerializedPaneGroup {
.read_with(cx, |pane, _| pane.items_len() != 0) .read_with(cx, |pane, _| pane.items_len() != 0)
.log_err()? .log_err()?
{ {
let pane = pane.upgrade(cx)?;
Some((Member::Pane(pane.clone()), active.then(|| pane))) Some((Member::Pane(pane.clone()), active.then(|| pane)))
} else { } else {
let pane = pane.upgrade(cx)?;
workspace workspace
.update(cx, |workspace, cx| workspace.remove_pane(pane, cx)) .update(cx, |workspace, cx| workspace.remove_pane(pane, cx))
.log_err()?; .log_err()?;
@ -172,7 +170,7 @@ impl SerializedPane {
pub async fn deserialize_to( pub async fn deserialize_to(
&self, &self,
project: &ModelHandle<Project>, project: &ModelHandle<Project>,
pane_handle: &ViewHandle<Pane>, pane_handle: &WeakViewHandle<Pane>,
workspace_id: WorkspaceId, workspace_id: WorkspaceId,
workspace: &WeakViewHandle<Workspace>, workspace: &WeakViewHandle<Workspace>,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
@ -196,8 +194,12 @@ impl SerializedPane {
if let Some(item_handle) = item_handle { if let Some(item_handle) = item_handle {
workspace.update(cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {
let pane_handle = pane_handle
.upgrade(cx)
.ok_or_else(|| anyhow!("pane was dropped"))?;
Pane::add_item(workspace, &pane_handle, item_handle, false, false, None, cx); Pane::add_item(workspace, &pane_handle, item_handle, false, false, None, cx);
})?; anyhow::Ok(())
})??;
} }
if item.active { if item.active {

View file

@ -864,7 +864,7 @@ impl Workspace {
requesting_window_id: Option<usize>, requesting_window_id: Option<usize>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Task<( ) -> Task<(
ViewHandle<Workspace>, WeakViewHandle<Workspace>,
Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>, Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
)> { )> {
let project_handle = Project::local( let project_handle = Project::local(
@ -982,6 +982,7 @@ impl Workspace {
.1 .1
}); });
let workspace = workspace.downgrade();
notify_if_database_failed(&workspace, &mut cx); notify_if_database_failed(&workspace, &mut cx);
// Call open path for each of the project paths // Call open path for each of the project paths
@ -1206,7 +1207,7 @@ impl Workspace {
.flat_map(|pane| { .flat_map(|pane| {
pane.read(cx).items().filter_map(|item| { pane.read(cx).items().filter_map(|item| {
if item.is_dirty(cx) { if item.is_dirty(cx) {
Some((pane.clone(), item.boxed_clone())) Some((pane.downgrade(), item.boxed_clone()))
} else { } else {
None None
} }
@ -2688,7 +2689,7 @@ impl Workspace {
workspace.read_with(&cx, |workspace, _| { workspace.read_with(&cx, |workspace, _| {
( (
workspace.project().clone(), workspace.project().clone(),
workspace.dock_pane().clone(), workspace.dock_pane().downgrade(),
workspace.last_active_center_pane.clone(), workspace.last_active_center_pane.clone(),
) )
})?; })?;
@ -2769,7 +2770,7 @@ impl Workspace {
} }
} }
fn notify_if_database_failed(workspace: &ViewHandle<Workspace>, cx: &mut AsyncAppContext) { fn notify_if_database_failed(workspace: &WeakViewHandle<Workspace>, cx: &mut AsyncAppContext) {
workspace.update(cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {
if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
workspace.show_notification_once(0, cx, |cx| { workspace.show_notification_once(0, cx, |cx| {
@ -2980,7 +2981,7 @@ pub struct WorkspaceCreated(WeakViewHandle<Workspace>);
pub fn activate_workspace_for_project( pub fn activate_workspace_for_project(
cx: &mut AppContext, cx: &mut AppContext,
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool, predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
) -> Option<ViewHandle<Workspace>> { ) -> Option<WeakViewHandle<Workspace>> {
for window_id in cx.window_ids().collect::<Vec<_>>() { for window_id in cx.window_ids().collect::<Vec<_>>() {
let handle = cx let handle = cx
.update_window(window_id, |cx| { .update_window(window_id, |cx| {
@ -2995,8 +2996,8 @@ pub fn activate_workspace_for_project(
}) })
.flatten(); .flatten();
if handle.is_some() { if let Some(handle) = handle {
return handle; return Some(handle.downgrade());
} }
} }
None None
@ -3014,7 +3015,7 @@ pub fn open_paths(
cx: &mut AppContext, cx: &mut AppContext,
) -> Task< ) -> Task<
Result<( Result<(
ViewHandle<Workspace>, WeakViewHandle<Workspace>,
Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>, Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
)>, )>,
> { > {
@ -3700,7 +3701,7 @@ mod tests {
workspace workspace
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
Pane::go_back(workspace, Some(pane.clone()), cx) Pane::go_back(workspace, Some(pane.downgrade()), cx)
}) })
.await .await
.unwrap(); .unwrap();

View file

@ -674,13 +674,15 @@ async fn handle_cli_connection(
let wait = async move { let wait = async move {
if paths.is_empty() { if paths.is_empty() {
let (done_tx, done_rx) = oneshot::channel(); let (done_tx, done_rx) = oneshot::channel();
let _subscription = cx.update(|cx| { if let Some(workspace) = workspace.upgrade(&cx) {
cx.observe_release(&workspace, move |_, _| { let _subscription = cx.update(|cx| {
let _ = done_tx.send(()); cx.observe_release(&workspace, move |_, _| {
}) let _ = done_tx.send(());
}); })
drop(workspace); });
let _ = done_rx.await; drop(workspace);
let _ = done_rx.await;
}
} else { } else {
let _ = let _ =
futures::future::try_join_all(item_release_futures).await; futures::future::try_join_all(item_release_futures).await;

View file

@ -374,7 +374,14 @@ pub fn build_window_options(
fn restart(_: &Restart, cx: &mut gpui::AppContext) { fn restart(_: &Restart, cx: &mut gpui::AppContext) {
let mut workspaces = cx let mut workspaces = cx
.window_ids() .window_ids()
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>()) .filter_map(|window_id| {
Some(
cx.root_view(window_id)?
.clone()
.downcast::<Workspace>()?
.downgrade(),
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// If multiple windows have unsaved changes, and need a save prompt, // If multiple windows have unsaved changes, and need a save prompt,
@ -419,7 +426,14 @@ fn restart(_: &Restart, cx: &mut gpui::AppContext) {
fn quit(_: &Quit, cx: &mut gpui::AppContext) { fn quit(_: &Quit, cx: &mut gpui::AppContext) {
let mut workspaces = cx let mut workspaces = cx
.window_ids() .window_ids()
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>()) .filter_map(|window_id| {
Some(
cx.root_view(window_id)?
.clone()
.downcast::<Workspace>()?
.downgrade(),
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// If multiple windows have unsaved changes, and need a save prompt, // If multiple windows have unsaved changes, and need a save prompt,