Ensure worktrees have been sent before responding with definitions

Changing the frequency at which we update worktrees highlighted a
problem in the randomized tests that was causing clients to receive
a definition to a worktree *before* observing the registration of
the worktree itself. This was most likely caused by #1224 because
the scenario that pull request enabled was the following:

- Guest requests a definition pointing to a non-existant worktree
- Server forwards the request to the host
- Host sends an `UpdateProject` message
- Host sends a response to the definition request
- Server observes the `UpdateProject` message and tries to acquire
  the store
- Given that we're waiting, the server goes ahead to process the
  response for the definition request, responding *before*
  `UpdateProject` is forwarded
- Server finally forwards `UpdateProject` to the guest

This commit ensures that, after forwarding a project request and getting a
response, we acquire a lock to the store again to ensure the project still
exists. This has the effect of ordering the forwarded request *after* any
message that was received prior to the response and for which we are still
waiting to acquire a lock to the store.
This commit is contained in:
Antonio Scandurra 2022-07-01 11:45:30 +02:00
parent 8a105bf12f
commit d36a4888db
2 changed files with 49 additions and 76 deletions

View file

@ -50,7 +50,6 @@ use std::{
time::Duration,
};
use theme::ThemeRegistry;
use tokio::sync::RwLockReadGuard;
use workspace::{Item, SplitDirection, ToggleFollow, Workspace};
#[ctor::ctor]
@ -589,7 +588,7 @@ async fn test_offline_projects(
deterministic.run_until_parked();
assert!(server
.store
.read()
.lock()
.await
.project_metadata_for_user(user_a)
.is_empty());
@ -620,7 +619,7 @@ async fn test_offline_projects(
cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
assert!(server
.store
.read()
.lock()
.await
.project_metadata_for_user(user_a)
.is_empty());
@ -1446,7 +1445,7 @@ async fn test_collaborating_with_diagnostics(
// Wait for server to see the diagnostics update.
deterministic.run_until_parked();
{
let store = server.store.read().await;
let store = server.store.lock().await;
let project = store.project(ProjectId::from_proto(project_id)).unwrap();
let worktree = project.worktrees.get(&worktree_id.to_proto()).unwrap();
assert!(!worktree.diagnostic_summaries.is_empty());
@ -3172,7 +3171,7 @@ async fn test_basic_chat(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
assert_eq!(
server
.state()
.store()
.await
.channel(channel_id)
.unwrap()
@ -4660,7 +4659,7 @@ async fn test_random_collaboration(
.unwrap();
let contacts = server
.store
.read()
.lock()
.await
.build_initial_contacts_update(contacts)
.contacts;
@ -4745,7 +4744,7 @@ async fn test_random_collaboration(
let contacts = server.app_state.db.get_contacts(*user_id).await.unwrap();
let contacts = server
.store
.read()
.lock()
.await
.build_initial_contacts_update(contacts)
.contacts;
@ -5077,10 +5076,6 @@ impl TestServer {
})
}
async fn state<'a>(&'a self) -> RwLockReadGuard<'a, Store> {
self.server.store.read().await
}
async fn condition<F>(&mut self, mut predicate: F)
where
F: FnMut(&Store) -> bool,
@ -5089,7 +5084,7 @@ impl TestServer {
self.foreground.parking_forbidden(),
"you must call forbid_parking to use server conditions so we don't block indefinitely"
);
while !(predicate)(&*self.server.store.read().await) {
while !(predicate)(&*self.server.store.lock().await) {
self.foreground.start_waiting();
self.notifications.next().await;
self.foreground.finish_waiting();