Add remote worktree to project before it is fully deserialized
This prevents a race condition where the host will send us messages and responses about a worktree that we have seen but haven't yet finished loading.
This commit is contained in:
parent
245490f934
commit
a762f575f4
2 changed files with 82 additions and 80 deletions
|
@ -247,8 +247,10 @@ impl Project {
|
||||||
|
|
||||||
let mut worktrees = Vec::new();
|
let mut worktrees = Vec::new();
|
||||||
for worktree in response.worktrees {
|
for worktree in response.worktrees {
|
||||||
worktrees
|
let (worktree, load_task) = cx
|
||||||
.push(Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx).await?);
|
.update(|cx| Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx));
|
||||||
|
worktrees.push(worktree);
|
||||||
|
load_task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_ids = response
|
let user_ids = response
|
||||||
|
@ -1464,16 +1466,9 @@ impl Project {
|
||||||
.payload
|
.payload
|
||||||
.worktree
|
.worktree
|
||||||
.ok_or_else(|| anyhow!("invalid worktree"))?;
|
.ok_or_else(|| anyhow!("invalid worktree"))?;
|
||||||
cx.spawn(|this, mut cx| {
|
let (worktree, load_task) = Worktree::remote(remote_id, replica_id, worktree, client, cx);
|
||||||
async move {
|
self.add_worktree(&worktree, cx);
|
||||||
let worktree =
|
load_task.detach();
|
||||||
Worktree::remote(remote_id, replica_id, worktree, client, &mut cx).await?;
|
|
||||||
this.update(&mut cx, |this, cx| this.add_worktree(&worktree, cx));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
.log_err()
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2551,15 +2546,16 @@ mod tests {
|
||||||
|
|
||||||
// Create a remote copy of this worktree.
|
// Create a remote copy of this worktree.
|
||||||
let initial_snapshot = tree.read_with(&cx, |tree, _| tree.snapshot());
|
let initial_snapshot = tree.read_with(&cx, |tree, _| tree.snapshot());
|
||||||
let remote = Worktree::remote(
|
let (remote, load_task) = cx.update(|cx| {
|
||||||
1,
|
Worktree::remote(
|
||||||
1,
|
1,
|
||||||
initial_snapshot.to_proto(&Default::default(), Default::default()),
|
1,
|
||||||
rpc.clone(),
|
initial_snapshot.to_proto(&Default::default(), Default::default()),
|
||||||
&mut cx.to_async(),
|
rpc.clone(),
|
||||||
)
|
cx,
|
||||||
.await
|
)
|
||||||
.unwrap();
|
});
|
||||||
|
load_task.await;
|
||||||
|
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
assert!(!buffer2.read(cx).is_dirty());
|
assert!(!buffer2.read(cx).is_dirty());
|
||||||
|
|
|
@ -190,13 +190,13 @@ impl Worktree {
|
||||||
Ok(tree)
|
Ok(tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remote(
|
pub fn remote(
|
||||||
project_remote_id: u64,
|
project_remote_id: u64,
|
||||||
replica_id: ReplicaId,
|
replica_id: ReplicaId,
|
||||||
worktree: proto::Worktree,
|
worktree: proto::Worktree,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
cx: &mut AsyncAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Result<ModelHandle<Self>> {
|
) -> (ModelHandle<Self>, Task<()>) {
|
||||||
let remote_id = worktree.id;
|
let remote_id = worktree.id;
|
||||||
let root_char_bag: CharBag = worktree
|
let root_char_bag: CharBag = worktree
|
||||||
.root_name
|
.root_name
|
||||||
|
@ -205,32 +205,26 @@ impl Worktree {
|
||||||
.collect();
|
.collect();
|
||||||
let root_name = worktree.root_name.clone();
|
let root_name = worktree.root_name.clone();
|
||||||
let weak = worktree.weak;
|
let weak = worktree.weak;
|
||||||
let (entries_by_path, entries_by_id, diagnostic_summaries) = cx
|
let snapshot = Snapshot {
|
||||||
.background()
|
id: WorktreeId(remote_id as usize),
|
||||||
.spawn(async move {
|
root_name,
|
||||||
let mut entries_by_path_edits = Vec::new();
|
root_char_bag,
|
||||||
let mut entries_by_id_edits = Vec::new();
|
entries_by_path: Default::default(),
|
||||||
for entry in worktree.entries {
|
entries_by_id: Default::default(),
|
||||||
match Entry::try_from((&root_char_bag, entry)) {
|
};
|
||||||
Ok(entry) => {
|
|
||||||
entries_by_id_edits.push(Edit::Insert(PathEntry {
|
|
||||||
id: entry.id,
|
|
||||||
path: entry.path.clone(),
|
|
||||||
is_ignored: entry.is_ignored,
|
|
||||||
scan_id: 0,
|
|
||||||
}));
|
|
||||||
entries_by_path_edits.push(Edit::Insert(entry));
|
|
||||||
}
|
|
||||||
Err(err) => log::warn!("error for remote worktree entry {:?}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries_by_path = SumTree::new();
|
let (updates_tx, mut updates_rx) = postage::mpsc::channel(64);
|
||||||
let mut entries_by_id = SumTree::new();
|
let (mut snapshot_tx, snapshot_rx) = watch::channel_with(snapshot.clone());
|
||||||
entries_by_path.edit(entries_by_path_edits, &());
|
let worktree_handle = cx.add_model(|_: &mut ModelContext<Worktree>| {
|
||||||
entries_by_id.edit(entries_by_id_edits, &());
|
Worktree::Remote(RemoteWorktree {
|
||||||
|
project_id: project_remote_id,
|
||||||
let diagnostic_summaries = TreeMap::from_ordered_entries(
|
replica_id,
|
||||||
|
snapshot: snapshot.clone(),
|
||||||
|
snapshot_rx: snapshot_rx.clone(),
|
||||||
|
updates_tx,
|
||||||
|
client: client.clone(),
|
||||||
|
queued_operations: Default::default(),
|
||||||
|
diagnostic_summaries: TreeMap::from_ordered_entries(
|
||||||
worktree.diagnostic_summaries.into_iter().map(|summary| {
|
worktree.diagnostic_summaries.into_iter().map(|summary| {
|
||||||
(
|
(
|
||||||
PathKey(PathBuf::from(summary.path).into()),
|
PathKey(PathBuf::from(summary.path).into()),
|
||||||
|
@ -242,24 +236,48 @@ impl Worktree {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
);
|
),
|
||||||
|
weak,
|
||||||
(entries_by_path, entries_by_id, diagnostic_summaries)
|
|
||||||
})
|
})
|
||||||
.await;
|
});
|
||||||
|
|
||||||
let worktree = cx.update(|cx| {
|
let deserialize_task = cx.spawn({
|
||||||
cx.add_model(|cx: &mut ModelContext<Worktree>| {
|
let worktree_handle = worktree_handle.clone();
|
||||||
let snapshot = Snapshot {
|
|cx| async move {
|
||||||
id: WorktreeId(remote_id as usize),
|
let (entries_by_path, entries_by_id) = cx
|
||||||
root_name,
|
.background()
|
||||||
root_char_bag,
|
.spawn(async move {
|
||||||
entries_by_path,
|
let mut entries_by_path_edits = Vec::new();
|
||||||
entries_by_id,
|
let mut entries_by_id_edits = Vec::new();
|
||||||
};
|
for entry in worktree.entries {
|
||||||
|
match Entry::try_from((&root_char_bag, entry)) {
|
||||||
|
Ok(entry) => {
|
||||||
|
entries_by_id_edits.push(Edit::Insert(PathEntry {
|
||||||
|
id: entry.id,
|
||||||
|
path: entry.path.clone(),
|
||||||
|
is_ignored: entry.is_ignored,
|
||||||
|
scan_id: 0,
|
||||||
|
}));
|
||||||
|
entries_by_path_edits.push(Edit::Insert(entry));
|
||||||
|
}
|
||||||
|
Err(err) => log::warn!("error for remote worktree entry {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (updates_tx, mut updates_rx) = postage::mpsc::channel(64);
|
let mut entries_by_path = SumTree::new();
|
||||||
let (mut snapshot_tx, snapshot_rx) = watch::channel_with(snapshot.clone());
|
let mut entries_by_id = SumTree::new();
|
||||||
|
entries_by_path.edit(entries_by_path_edits, &());
|
||||||
|
entries_by_id.edit(entries_by_id_edits, &());
|
||||||
|
|
||||||
|
(entries_by_path, entries_by_id)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut snapshot = snapshot_tx.borrow_mut();
|
||||||
|
snapshot.entries_by_path = entries_by_path;
|
||||||
|
snapshot.entries_by_id = entries_by_id;
|
||||||
|
}
|
||||||
|
|
||||||
cx.background()
|
cx.background()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
|
@ -275,7 +293,8 @@ impl Worktree {
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut snapshot_rx = snapshot_rx.clone();
|
let mut snapshot_rx = snapshot_rx.clone();
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
let this = worktree_handle.downgrade();
|
||||||
|
cx.spawn(|mut cx| async move {
|
||||||
while let Some(_) = snapshot_rx.recv().await {
|
while let Some(_) = snapshot_rx.recv().await {
|
||||||
if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
|
if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
|
||||||
this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
|
this.update(&mut cx, |this, cx| this.poll_snapshot(cx));
|
||||||
|
@ -286,22 +305,9 @@ impl Worktree {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Worktree::Remote(RemoteWorktree {
|
|
||||||
project_id: project_remote_id,
|
|
||||||
replica_id,
|
|
||||||
snapshot,
|
|
||||||
snapshot_rx,
|
|
||||||
updates_tx,
|
|
||||||
client: client.clone(),
|
|
||||||
queued_operations: Default::default(),
|
|
||||||
diagnostic_summaries,
|
|
||||||
weak,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
(worktree_handle, deserialize_task)
|
||||||
Ok(worktree)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_local(&self) -> Option<&LocalWorktree> {
|
pub fn as_local(&self) -> Option<&LocalWorktree> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue