@@ -21,6 +21,7 @@ use dashmap::DashMap;
use futures::StreamExt;
use hyper::StatusCode;
use rpc::{proto, ConnectionId};
+use sea_orm::Condition;
pub use sea_orm::ConnectOptions;
use sea_orm::{
entity::prelude::*, ActiveValue, ConnectionTrait, DatabaseConnection, DatabaseTransaction,
@@ -47,7 +48,7 @@ pub struct Database {
background: Option<std::sync::Arc<gpui::executor::Background>>,
#[cfg(test)]
runtime: Option<tokio::runtime::Runtime>,
- epoch: Uuid,
+ epoch: parking_lot::RwLock<Uuid>,
}
impl Database {
@@ -60,10 +61,20 @@ impl Database {
background: None,
#[cfg(test)]
runtime: None,
- epoch: Uuid::new_v4(),
+ epoch: parking_lot::RwLock::new(Uuid::new_v4()),
})
}
+ #[cfg(test)]
+ pub fn reset(&self) {
+ self.rooms.clear();
+ *self.epoch.write() = Uuid::new_v4();
+ }
+
+ fn epoch(&self) -> Uuid {
+ *self.epoch.read()
+ }
+
pub async fn migrate(
&self,
migrations_path: &Path,
@@ -105,37 +116,85 @@ impl Database {
Ok(new_migrations)
}
- pub async fn clear_stale_data(&self) -> Result<()> {
+ pub async fn delete_stale_projects(&self) -> Result<()> {
self.transaction(|tx| async move {
project_collaborator::Entity::delete_many()
- .filter(project_collaborator::Column::ConnectionEpoch.ne(self.epoch))
- .exec(&*tx)
- .await?;
- room_participant::Entity::delete_many()
- .filter(
- room_participant::Column::AnsweringConnectionEpoch
- .ne(self.epoch)
- .or(room_participant::Column::CallingConnectionEpoch.ne(self.epoch)),
- )
+ .filter(project_collaborator::Column::ConnectionEpoch.ne(self.epoch()))
.exec(&*tx)
.await?;
project::Entity::delete_many()
- .filter(project::Column::HostConnectionEpoch.ne(self.epoch))
+ .filter(project::Column::HostConnectionEpoch.ne(self.epoch()))
.exec(&*tx)
.await?;
- room::Entity::delete_many()
- .filter(
- room::Column::Id.not_in_subquery(
- Query::select()
- .column(room_participant::Column::RoomId)
- .from(room_participant::Entity)
- .distinct()
- .to_owned(),
- ),
- )
+ Ok(())
+ })
+ .await
+ }
+
+ pub async fn outdated_room_ids(&self) -> Result<Vec<RoomId>> {
+ self.transaction(|tx| async move {
+ #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
+ enum QueryAs {
+ RoomId,
+ }
+
+ Ok(room_participant::Entity::find()
+ .select_only()
+ .column(room_participant::Column::RoomId)
+ .distinct()
+ .filter(room_participant::Column::AnsweringConnectionEpoch.ne(self.epoch()))
+ .into_values::<_, QueryAs>()
+ .all(&*tx)
+ .await?)
+ })
+ .await
+ }
+
+ pub async fn refresh_room(&self, room_id: RoomId) -> Result<RoomGuard<RefreshedRoom>> {
+ self.room_transaction(|tx| async move {
+ let stale_participant_filter = Condition::all()
+ .add(room_participant::Column::RoomId.eq(room_id))
+ .add(room_participant::Column::AnsweringConnectionId.is_not_null())
+ .add(room_participant::Column::AnsweringConnectionEpoch.ne(self.epoch()));
+
+ let stale_participant_user_ids = room_participant::Entity::find()
+ .filter(stale_participant_filter.clone())
+ .all(&*tx)
+ .await?
+ .into_iter()
+ .map(|participant| participant.user_id)
+ .collect::<Vec<_>>();
+
+ // Delete participants who failed to reconnect.
+ room_participant::Entity::delete_many()
+ .filter(stale_participant_filter)
.exec(&*tx)
.await?;
- Ok(())
+
+ let room = self.get_room(room_id, &tx).await?;
+ let mut canceled_calls_to_user_ids = Vec::new();
+ // Delete the room if it becomes empty and cancel pending calls.
+ if room.participants.is_empty() {
+ canceled_calls_to_user_ids.extend(
+ room.pending_participants
+ .iter()
+ .map(|pending_participant| UserId::from_proto(pending_participant.user_id)),
+ );
+ room_participant::Entity::delete_many()
+ .filter(room_participant::Column::RoomId.eq(room_id))
+ .exec(&*tx)
+ .await?;
+ room::Entity::delete_by_id(room_id).exec(&*tx).await?;
+ }
+
+ Ok((
+ room_id,
+ RefreshedRoom {
+ room,
+ stale_participant_user_ids,
+ canceled_calls_to_user_ids,
+ },
+ ))
})
.await
}
@@ -1033,11 +1092,11 @@ impl Database {
room_id: ActiveValue::set(room_id),
user_id: ActiveValue::set(user_id),
answering_connection_id: ActiveValue::set(Some(connection_id.0 as i32)),
- answering_connection_epoch: ActiveValue::set(Some(self.epoch)),
+ answering_connection_epoch: ActiveValue::set(Some(self.epoch())),
answering_connection_lost: ActiveValue::set(false),
calling_user_id: ActiveValue::set(user_id),
calling_connection_id: ActiveValue::set(connection_id.0 as i32),
- calling_connection_epoch: ActiveValue::set(self.epoch),
+ calling_connection_epoch: ActiveValue::set(self.epoch()),
..Default::default()
}
.insert(&*tx)
@@ -1064,7 +1123,7 @@ impl Database {
answering_connection_lost: ActiveValue::set(false),
calling_user_id: ActiveValue::set(calling_user_id),
calling_connection_id: ActiveValue::set(calling_connection_id.0 as i32),
- calling_connection_epoch: ActiveValue::set(self.epoch),
+ calling_connection_epoch: ActiveValue::set(self.epoch()),
initial_project_id: ActiveValue::set(initial_project_id),
..Default::default()
}
@@ -1174,18 +1233,22 @@ impl Database {
self.room_transaction(|tx| async move {
let result = room_participant::Entity::update_many()
.filter(
- room_participant::Column::RoomId
- .eq(room_id)
- .and(room_participant::Column::UserId.eq(user_id))
- .and(
- room_participant::Column::AnsweringConnectionId
- .is_null()
- .or(room_participant::Column::AnsweringConnectionLost.eq(true)),
+ Condition::all()
+ .add(room_participant::Column::RoomId.eq(room_id))
+ .add(room_participant::Column::UserId.eq(user_id))
+ .add(
+ Condition::any()
+ .add(room_participant::Column::AnsweringConnectionId.is_null())
+ .add(room_participant::Column::AnsweringConnectionLost.eq(true))
+ .add(
+ room_participant::Column::AnsweringConnectionEpoch
+ .ne(self.epoch()),
+ ),
),
)
.set(room_participant::ActiveModel {
answering_connection_id: ActiveValue::set(Some(connection_id.0 as i32)),
- answering_connection_epoch: ActiveValue::set(Some(self.epoch)),
+ answering_connection_epoch: ActiveValue::set(Some(self.epoch())),
answering_connection_lost: ActiveValue::set(false),
..Default::default()
})
@@ -1591,7 +1654,7 @@ impl Database {
room_id: ActiveValue::set(participant.room_id),
host_user_id: ActiveValue::set(participant.user_id),
host_connection_id: ActiveValue::set(connection_id.0 as i32),
- host_connection_epoch: ActiveValue::set(self.epoch),
+ host_connection_epoch: ActiveValue::set(self.epoch()),
..Default::default()
}
.insert(&*tx)
@@ -1616,7 +1679,7 @@ impl Database {
project_collaborator::ActiveModel {
project_id: ActiveValue::set(project.id),
connection_id: ActiveValue::set(connection_id.0 as i32),
- connection_epoch: ActiveValue::set(self.epoch),
+ connection_epoch: ActiveValue::set(self.epoch()),
user_id: ActiveValue::set(participant.user_id),
replica_id: ActiveValue::set(ReplicaId(0)),
is_host: ActiveValue::set(true),
@@ -1930,7 +1993,7 @@ impl Database {
let new_collaborator = project_collaborator::ActiveModel {
project_id: ActiveValue::set(project_id),
connection_id: ActiveValue::set(connection_id.0 as i32),
- connection_epoch: ActiveValue::set(self.epoch),
+ connection_epoch: ActiveValue::set(self.epoch()),
user_id: ActiveValue::set(participant.user_id),
replica_id: ActiveValue::set(replica_id),
is_host: ActiveValue::set(false),
@@ -2553,6 +2616,12 @@ pub struct LeftRoom {
pub canceled_calls_to_user_ids: Vec<UserId>,
}
+pub struct RefreshedRoom {
+ pub room: proto::Room,
+ pub stale_participant_user_ids: Vec<UserId>,
+ pub canceled_calls_to_user_ids: Vec<UserId>,
+}
+
pub struct Project {
pub collaborators: Vec<project_collaborator::Model>,
pub worktrees: BTreeMap<u64, Worktree>,
@@ -4,7 +4,6 @@ use crate::{
rpc::{Server, RECONNECT_TIMEOUT},
AppState,
};
-use ::rpc::Peer;
use anyhow::anyhow;
use call::{room, ActiveCall, ParticipantLocation, Room};
use client::{
@@ -19,10 +18,8 @@ use editor::{
use fs::{FakeFs, Fs as _, HomeDir, LineEnding};
use futures::{channel::oneshot, StreamExt as _};
use gpui::{
- executor::{self, Deterministic},
- geometry::vector::vec2f,
- test::EmptyView,
- ModelHandle, Task, TestAppContext, ViewHandle,
+ executor::Deterministic, geometry::vector::vec2f, test::EmptyView, ModelHandle, Task,
+ TestAppContext, ViewHandle,
};
use language::{
range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
@@ -37,7 +34,7 @@ use serde_json::json;
use settings::{Formatter, Settings};
use std::{
cell::{Cell, RefCell},
- env, mem,
+ env, future, mem,
ops::Deref,
path::{Path, PathBuf},
rc::Rc,
@@ -67,7 +64,7 @@ async fn test_basic_calls(
cx_c: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -266,7 +263,7 @@ async fn test_room_uniqueness(
cx_c: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let _client_a2 = server.create_client(cx_a2, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -365,13 +362,13 @@ async fn test_room_uniqueness(
}
#[gpui::test(iterations = 10)]
-async fn test_disconnecting_from_room(
+async fn test_client_disconnecting_from_room(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -516,6 +513,303 @@ async fn test_disconnecting_from_room(
);
}
+#[gpui::test(iterations = 10)]
+async fn test_server_restarts(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+ cx_c: &mut TestAppContext,
+ cx_d: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
+ let client_a = server.create_client(cx_a, "user_a").await;
+ let client_b = server.create_client(cx_b, "user_b").await;
+ let client_c = server.create_client(cx_c, "user_c").await;
+ let client_d = server.create_client(cx_d, "user_d").await;
+ server
+ .make_contacts(&mut [
+ (&client_a, cx_a),
+ (&client_b, cx_b),
+ (&client_c, cx_c),
+ (&client_d, cx_d),
+ ])
+ .await;
+
+ let active_call_a = cx_a.read(ActiveCall::global);
+ let active_call_b = cx_b.read(ActiveCall::global);
+ let active_call_c = cx_c.read(ActiveCall::global);
+ let active_call_d = cx_d.read(ActiveCall::global);
+
+ // User A calls users B, C, and D.
+ active_call_a
+ .update(cx_a, |call, cx| {
+ call.invite(client_b.user_id().unwrap(), None, cx)
+ })
+ .await
+ .unwrap();
+ active_call_a
+ .update(cx_a, |call, cx| {
+ call.invite(client_c.user_id().unwrap(), None, cx)
+ })
+ .await
+ .unwrap();
+ active_call_a
+ .update(cx_a, |call, cx| {
+ call.invite(client_d.user_id().unwrap(), None, cx)
+ })
+ .await
+ .unwrap();
+ let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
+
+ // User B receives the call and joins the room.
+ let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
+ assert!(incoming_call_b.next().await.unwrap().is_some());
+ active_call_b
+ .update(cx_b, |call, cx| call.accept_incoming(cx))
+ .await
+ .unwrap();
+ let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
+
+ // User C receives the call and joins the room.
+ let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
+ assert!(incoming_call_c.next().await.unwrap().is_some());
+ active_call_c
+ .update(cx_c, |call, cx| call.accept_incoming(cx))
+ .await
+ .unwrap();
+ let room_c = active_call_c.read_with(cx_c, |call, _| call.room().unwrap().clone());
+
+ // User D receives the call but doesn't join the room yet.
+ let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
+ assert!(incoming_call_d.next().await.unwrap().is_some());
+
+ deterministic.run_until_parked();
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec!["user_b".to_string(), "user_c".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec!["user_a".to_string(), "user_c".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_c, cx_c),
+ RoomParticipants {
+ remote: vec!["user_a".to_string(), "user_b".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+
+ // The server is torn down.
+ server.teardown();
+
+ // Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room.
+ client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
+ deterministic.advance_clock(RECEIVE_TIMEOUT);
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec!["user_b".to_string(), "user_c".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec!["user_a".to_string(), "user_c".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_c, cx_c),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+
+ // User D is notified again of the incoming call and accepts it.
+ assert!(incoming_call_d.next().await.unwrap().is_some());
+ active_call_d
+ .update(cx_d, |call, cx| call.accept_incoming(cx))
+ .await
+ .unwrap();
+ deterministic.run_until_parked();
+ let room_d = active_call_d.read_with(cx_d, |call, _| call.room().unwrap().clone());
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec![
+ "user_b".to_string(),
+ "user_c".to_string(),
+ "user_d".to_string(),
+ ],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec![
+ "user_a".to_string(),
+ "user_c".to_string(),
+ "user_d".to_string(),
+ ],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_c, cx_c),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_d, cx_d),
+ RoomParticipants {
+ remote: vec![
+ "user_a".to_string(),
+ "user_b".to_string(),
+ "user_c".to_string(),
+ ],
+ pending: vec![]
+ }
+ );
+
+ // The server finishes restarting, cleaning up stale connections.
+ server.start().await.unwrap();
+ deterministic.advance_clock(RECONNECT_TIMEOUT);
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec!["user_b".to_string(), "user_d".to_string()],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec!["user_a".to_string(), "user_d".to_string()],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_c, cx_c),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_d, cx_d),
+ RoomParticipants {
+ remote: vec!["user_a".to_string(), "user_b".to_string()],
+ pending: vec![]
+ }
+ );
+
+ // User D hangs up.
+ active_call_d
+ .update(cx_d, |call, cx| call.hang_up(cx))
+ .unwrap();
+ deterministic.run_until_parked();
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec!["user_b".to_string()],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec!["user_a".to_string()],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_c, cx_c),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_d, cx_d),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+
+ // User B calls user D again.
+ active_call_b
+ .update(cx_b, |call, cx| {
+ call.invite(client_d.user_id().unwrap(), None, cx)
+ })
+ .await
+ .unwrap();
+
+ // User D receives the call but doesn't join the room yet.
+ let mut incoming_call_d = active_call_d.read_with(cx_d, |call, _| call.incoming());
+ assert!(incoming_call_d.next().await.unwrap().is_some());
+ deterministic.run_until_parked();
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec!["user_b".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec!["user_a".to_string()],
+ pending: vec!["user_d".to_string()]
+ }
+ );
+
+ // The server is torn down.
+ server.teardown();
+
+ // Users A and B have troubles reconnecting, so they leave the room.
+ client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
+ client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
+ client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
+ deterministic.advance_clock(RECEIVE_TIMEOUT);
+ assert_eq!(
+ room_participants(&room_a, cx_a),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+ assert_eq!(
+ room_participants(&room_b, cx_b),
+ RoomParticipants {
+ remote: vec![],
+ pending: vec![]
+ }
+ );
+
+ // User D is notified again of the incoming call but doesn't accept it.
+ assert!(incoming_call_d.next().await.unwrap().is_some());
+
+ // The server finishes restarting, cleaning up stale connections and canceling the
+ // call to user D because the room has become empty.
+ server.start().await.unwrap();
+ deterministic.advance_clock(RECONNECT_TIMEOUT);
+ assert!(incoming_call_d.next().await.unwrap().is_none());
+}
+
#[gpui::test(iterations = 10)]
async fn test_calls_on_multiple_connections(
deterministic: Arc<Deterministic>,
@@ -524,7 +818,7 @@ async fn test_calls_on_multiple_connections(
cx_b2: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b1 = server.create_client(cx_b1, "user_b").await;
let client_b2 = server.create_client(cx_b2, "user_b").await;
@@ -676,7 +970,7 @@ async fn test_share_project(
) {
deterministic.forbid_parking();
let (_, window_b) = cx_b.add_window(|_| EmptyView);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -813,7 +1107,7 @@ async fn test_unshare_project(
cx_c: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -896,7 +1190,7 @@ async fn test_host_disconnect(
) {
cx_b.update(editor::init);
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -1014,7 +1308,7 @@ async fn test_active_call_events(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
client_a.fs.insert_tree("/a", json!({})).await;
@@ -1103,7 +1397,7 @@ async fn test_room_location(
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
client_a.fs.insert_tree("/a", json!({})).await;
@@ -1269,7 +1563,7 @@ async fn test_propagate_saves_and_fs_changes(
cx_c: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -1439,12 +1733,12 @@ async fn test_propagate_saves_and_fs_changes(
#[gpui::test(iterations = 10)]
async fn test_git_diff_base_change(
- executor: Arc<Deterministic>,
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
- executor.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -1513,7 +1807,7 @@ async fn test_git_diff_base_change(
.unwrap();
// Wait for it to catch up to the new diff
- executor.run_until_parked();
+ deterministic.run_until_parked();
// Smoke test diffing
buffer_local_a.read_with(cx_a, |buffer, _| {
@@ -1533,7 +1827,7 @@ async fn test_git_diff_base_change(
.unwrap();
// Wait remote buffer to catch up to the new diff
- executor.run_until_parked();
+ deterministic.run_until_parked();
// Smoke test diffing
buffer_remote_a.read_with(cx_b, |buffer, _| {
@@ -1556,7 +1850,7 @@ async fn test_git_diff_base_change(
.await;
// Wait for buffer_local_a to receive it
- executor.run_until_parked();
+ deterministic.run_until_parked();
// Smoke test new diffing
buffer_local_a.read_with(cx_a, |buffer, _| {
@@ -1611,7 +1905,7 @@ async fn test_git_diff_base_change(
.unwrap();
// Wait for it to catch up to the new diff
- executor.run_until_parked();
+ deterministic.run_until_parked();
// Smoke test diffing
buffer_local_b.read_with(cx_a, |buffer, _| {
@@ -1631,7 +1925,7 @@ async fn test_git_diff_base_change(
.unwrap();
// Wait remote buffer to catch up to the new diff
- executor.run_until_parked();
+ deterministic.run_until_parked();
// Smoke test diffing
buffer_remote_b.read_with(cx_b, |buffer, _| {
@@ -1654,7 +1948,7 @@ async fn test_git_diff_base_change(
.await;
// Wait for buffer_local_b to receive it
- executor.run_until_parked();
+ deterministic.run_until_parked();
// Smoke test new diffing
buffer_local_b.read_with(cx_a, |buffer, _| {
@@ -1691,12 +1985,12 @@ async fn test_git_diff_base_change(
#[gpui::test(iterations = 10)]
async fn test_fs_operations(
- executor: Arc<Deterministic>,
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
- executor.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -1960,9 +2254,13 @@ async fn test_fs_operations(
}
#[gpui::test(iterations = 10)]
-async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_buffer_conflict_after_save(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2014,9 +2312,13 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T
}
#[gpui::test(iterations = 10)]
-async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_buffer_reloading(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2071,11 +2373,12 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
#[gpui::test(iterations = 10)]
async fn test_editing_while_guest_opens_buffer(
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2118,11 +2421,12 @@ async fn test_editing_while_guest_opens_buffer(
#[gpui::test(iterations = 10)]
async fn test_leaving_worktree_while_opening_buffer(
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2167,7 +2471,7 @@ async fn test_canceling_buffer_opening(
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2218,7 +2522,7 @@ async fn test_leaving_project(
cx_c: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -2329,7 +2633,7 @@ async fn test_leaving_project(
// Simulate connection loss for client C and ensure client A observes client C leaving the project.
client_c.wait_for_current_user(cx_c).await;
server.disconnect_client(client_c.peer_id().unwrap());
- cx_a.foreground().advance_clock(RECEIVE_TIMEOUT);
+ deterministic.advance_clock(RECEIVE_TIMEOUT);
deterministic.run_until_parked();
project_a.read_with(cx_a, |project, _| {
assert_eq!(project.collaborators().len(), 0);
@@ -2350,7 +2654,7 @@ async fn test_collaborating_with_diagnostics(
cx_c: &mut TestAppContext,
) {
deterministic.forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -2610,9 +2914,13 @@ async fn test_collaborating_with_diagnostics(
}
#[gpui::test(iterations = 10)]
-async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_collaborating_with_completion(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2784,9 +3092,13 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
}
#[gpui::test(iterations = 10)]
-async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_reloading_buffer_manually(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2876,10 +3188,14 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te
}
#[gpui::test(iterations = 10)]
-async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
+async fn test_formatting_buffer(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
use project::FormatTrigger;
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -2978,9 +3294,13 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
}
#[gpui::test(iterations = 10)]
-async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_definition(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3122,9 +3442,13 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
}
#[gpui::test(iterations = 10)]
-async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_references(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3223,9 +3547,13 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
}
#[gpui::test(iterations = 10)]
-async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_project_search(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3302,9 +3630,13 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex
}
#[gpui::test(iterations = 10)]
-async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_document_highlights(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3404,9 +3736,13 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC
}
#[gpui::test(iterations = 10)]
-async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_lsp_hover(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3507,9 +3843,13 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
}
#[gpui::test(iterations = 10)]
-async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+async fn test_project_symbols(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3612,12 +3952,13 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
#[gpui::test(iterations = 10)]
async fn test_open_buffer_while_getting_definition_pointing_to_it(
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
mut rng: StdRng,
) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3688,12 +4029,13 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
#[gpui::test(iterations = 10)]
async fn test_collaborating_with_code_actions(
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
- cx_a.foreground().forbid_parking();
+ deterministic.forbid_parking();
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -3908,10 +4250,14 @@ async fn test_collaborating_with_code_actions(
}
#[gpui::test(iterations = 10)]
-async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
+async fn test_collaborating_with_renames(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -4110,7 +4456,7 @@ async fn test_language_server_statuses(
deterministic.forbid_parking();
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -4222,8 +4568,8 @@ async fn test_contacts(
cx_c: &mut TestAppContext,
cx_d: &mut TestAppContext,
) {
- cx_a.foreground().forbid_parking();
- let mut server = TestServer::start(cx_a.background()).await;
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
@@ -4610,7 +4956,7 @@ async fn test_contacts(
#[gpui::test(iterations = 10)]
async fn test_contact_requests(
- executor: Arc<Deterministic>,
+ deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_a2: &mut TestAppContext,
cx_b: &mut TestAppContext,
@@ -4618,10 +4964,10 @@ async fn test_contact_requests(
cx_c: &mut TestAppContext,
cx_c2: &mut TestAppContext,
) {
- cx_a.foreground().forbid_parking();
+ deterministic.forbid_parking();
// Connect to a server as 3 clients.
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_a2 = server.create_client(cx_a2, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -4648,7 +4994,7 @@ async fn test_contact_requests(
})
.await
.unwrap();
- executor.run_until_parked();
+ deterministic.run_until_parked();
// All users see the pending request appear in all their clients.
assert_eq!(
@@ -4680,7 +5026,7 @@ async fn test_contact_requests(
disconnect_and_reconnect(&client_a, cx_a).await;
disconnect_and_reconnect(&client_b, cx_b).await;
disconnect_and_reconnect(&client_c, cx_c).await;
- executor.run_until_parked();
+ deterministic.run_until_parked();
assert_eq!(
client_a.summarize_contacts(cx_a).outgoing_requests,
&["user_b"]
@@ -4703,7 +5049,7 @@ async fn test_contact_requests(
.await
.unwrap();
- executor.run_until_parked();
+ deterministic.run_until_parked();
// User B sees user A as their contact now in all client, and the incoming request from them is removed.
let contacts_b = client_b.summarize_contacts(cx_b);
@@ -4725,7 +5071,7 @@ async fn test_contact_requests(
disconnect_and_reconnect(&client_a, cx_a).await;
disconnect_and_reconnect(&client_b, cx_b).await;
disconnect_and_reconnect(&client_c, cx_c).await;
- executor.run_until_parked();
+ deterministic.run_until_parked();
assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
assert_eq!(
@@ -4747,7 +5093,7 @@ async fn test_contact_requests(
.await
.unwrap();
- executor.run_until_parked();
+ deterministic.run_until_parked();
// User B doesn't see user C as their contact, and the incoming request from them is removed.
let contacts_b = client_b.summarize_contacts(cx_b);
@@ -4769,7 +5115,7 @@ async fn test_contact_requests(
disconnect_and_reconnect(&client_a, cx_a).await;
disconnect_and_reconnect(&client_b, cx_b).await;
disconnect_and_reconnect(&client_c, cx_c).await;
- executor.run_until_parked();
+ deterministic.run_until_parked();
assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]);
assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]);
assert!(client_b
@@ -4798,11 +5144,11 @@ async fn test_following(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
- cx_a.foreground().forbid_parking();
+ deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -5079,7 +5425,7 @@ async fn test_following_tab_order(
cx_a.update(editor::init);
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -5194,12 +5540,16 @@ async fn test_following_tab_order(
}
#[gpui::test(iterations = 10)]
-async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
+async fn test_peers_following_each_other(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -5371,13 +5721,17 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
}
#[gpui::test(iterations = 10)]
-async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
+async fn test_auto_unfollowing(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
cx_a.update(editor::init);
cx_b.update(editor::init);
// 2 clients connect to a server.
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -5551,7 +5905,7 @@ async fn test_peers_simultaneously_following_each_other(
cx_a.update(editor::init);
cx_b.update(editor::init);
- let mut server = TestServer::start(cx_a.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
@@ -5621,7 +5975,7 @@ async fn test_random_collaboration(
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
- let mut server = TestServer::start(cx.background()).await;
+ let mut server = TestServer::start(&deterministic).await;
let db = server.app_state.db.clone();
let mut available_guests = Vec::new();
@@ -5709,7 +6063,6 @@ async fn test_random_collaboration(
let user_connection_ids = server
.connection_pool
.lock()
- .await
.user_connection_ids(removed_guest_id)
.collect::<Vec<_>>();
assert_eq!(user_connection_ids.len(), 1);
@@ -53,7 +53,7 @@ use std::{
},
time::Duration,
};
-use tokio::sync::{watch, Mutex, MutexGuard};
+use tokio::sync::watch;
use tower::ServiceBuilder;
use tracing::{info_span, instrument, Instrument};
@@ -90,14 +90,14 @@ impl<R: RequestMessage> Response<R> {
struct Session {
user_id: UserId,
connection_id: ConnectionId,
- db: Arc<Mutex<DbHandle>>,
+ db: Arc<tokio::sync::Mutex<DbHandle>>,
peer: Arc<Peer>,
- connection_pool: Arc<Mutex<ConnectionPool>>,
+ connection_pool: Arc<parking_lot::Mutex<ConnectionPool>>,
live_kit_client: Option<Arc<dyn live_kit_server::api::Client>>,
}
impl Session {
- async fn db(&self) -> MutexGuard<DbHandle> {
+ async fn db(&self) -> tokio::sync::MutexGuard<DbHandle> {
#[cfg(test)]
tokio::task::yield_now().await;
let guard = self.db.lock().await;
@@ -109,9 +109,7 @@ impl Session {
async fn connection_pool(&self) -> ConnectionPoolGuard<'_> {
#[cfg(test)]
tokio::task::yield_now().await;
- let guard = self.connection_pool.lock().await;
- #[cfg(test)]
- tokio::task::yield_now().await;
+ let guard = self.connection_pool.lock();
ConnectionPoolGuard {
guard,
_not_send: PhantomData,
@@ -140,14 +138,15 @@ impl Deref for DbHandle {
pub struct Server {
peer: Arc<Peer>,
- pub(crate) connection_pool: Arc<Mutex<ConnectionPool>>,
+ pub(crate) connection_pool: Arc<parking_lot::Mutex<ConnectionPool>>,
app_state: Arc<AppState>,
+ executor: Executor,
handlers: HashMap<TypeId, MessageHandler>,
teardown: watch::Sender<()>,
}
pub(crate) struct ConnectionPoolGuard<'a> {
- guard: MutexGuard<'a, ConnectionPool>,
+ guard: parking_lot::MutexGuard<'a, ConnectionPool>,
_not_send: PhantomData<Rc<()>>,
}
@@ -168,10 +167,11 @@ where
}
impl Server {
- pub fn new(app_state: Arc<AppState>) -> Arc<Self> {
+ pub fn new(app_state: Arc<AppState>, executor: Executor) -> Arc<Self> {
let mut server = Self {
peer: Peer::new(),
app_state,
+ executor,
connection_pool: Default::default(),
handlers: Default::default(),
teardown: watch::channel(()).0,
@@ -237,7 +237,96 @@ impl Server {
Arc::new(server)
}
+ pub async fn start(&self) -> Result<()> {
+ self.app_state.db.delete_stale_projects().await?;
+ let db = self.app_state.db.clone();
+ let peer = self.peer.clone();
+ let timeout = self.executor.sleep(RECONNECT_TIMEOUT);
+ let pool = self.connection_pool.clone();
+ let live_kit_client = self.app_state.live_kit_client.clone();
+ self.executor.spawn_detached(async move {
+ timeout.await;
+ if let Some(room_ids) = db.outdated_room_ids().await.trace_err() {
+ for room_id in room_ids {
+ let mut contacts_to_update = HashSet::default();
+ let mut canceled_calls_to_user_ids = Vec::new();
+ let mut live_kit_room = String::new();
+ let mut delete_live_kit_room = false;
+
+ if let Ok(mut refreshed_room) = db.refresh_room(room_id).await {
+ room_updated(&refreshed_room.room, &peer);
+ contacts_to_update
+ .extend(refreshed_room.stale_participant_user_ids.iter().copied());
+ contacts_to_update
+ .extend(refreshed_room.canceled_calls_to_user_ids.iter().copied());
+ canceled_calls_to_user_ids =
+ mem::take(&mut refreshed_room.canceled_calls_to_user_ids);
+ live_kit_room = mem::take(&mut refreshed_room.room.live_kit_room);
+ delete_live_kit_room = refreshed_room.room.participants.is_empty();
+ }
+
+ {
+ let pool = pool.lock();
+ for canceled_user_id in canceled_calls_to_user_ids {
+ for connection_id in pool.user_connection_ids(canceled_user_id) {
+ peer.send(
+ connection_id,
+ proto::CallCanceled {
+ room_id: room_id.to_proto(),
+ },
+ )
+ .trace_err();
+ }
+ }
+ }
+
+ for user_id in contacts_to_update {
+ let busy = db.is_user_busy(user_id).await.trace_err();
+ let contacts = db.get_contacts(user_id).await.trace_err();
+ if let Some((busy, contacts)) = busy.zip(contacts) {
+ let pool = pool.lock();
+ let updated_contact = contact_for_user(user_id, false, busy, &pool);
+ for contact in contacts {
+ if let db::Contact::Accepted {
+ user_id: contact_user_id,
+ ..
+ } = contact
+ {
+ for contact_conn_id in pool.user_connection_ids(contact_user_id)
+ {
+ peer.send(
+ contact_conn_id,
+ proto::UpdateContacts {
+ contacts: vec![updated_contact.clone()],
+ remove_contacts: Default::default(),
+ incoming_requests: Default::default(),
+ remove_incoming_requests: Default::default(),
+ outgoing_requests: Default::default(),
+ remove_outgoing_requests: Default::default(),
+ },
+ )
+ .trace_err();
+ }
+ }
+ }
+ }
+ }
+
+ if let Some(live_kit) = live_kit_client.as_ref() {
+ if delete_live_kit_room {
+ live_kit.delete_room(live_kit_room).await.trace_err();
+ }
+ }
+ }
+ }
+ });
+ Ok(())
+ }
+
+ #[cfg(test)]
pub fn teardown(&self) {
+ self.peer.reset();
+ self.connection_pool.lock().reset();
let _ = self.teardown.send(());
}
@@ -339,7 +428,7 @@ impl Server {
let user_id = user.id;
let login = user.github_login;
let span = info_span!("handle connection", %user_id, %login, %address);
- let teardown = self.teardown.subscribe();
+ let mut teardown = self.teardown.subscribe();
async move {
let (connection_id, handle_io, mut incoming_rx) = this
.peer
@@ -367,7 +456,7 @@ impl Server {
).await?;
{
- let mut pool = this.connection_pool.lock().await;
+ let mut pool = this.connection_pool.lock();
pool.add_connection(connection_id, user_id, user.admin);
this.peer.send(connection_id, build_initial_contacts_update(contacts, &pool))?;
@@ -386,7 +475,7 @@ impl Server {
let session = Session {
user_id,
connection_id,
- db: Arc::new(Mutex::new(DbHandle(this.app_state.db.clone()))),
+ db: Arc::new(tokio::sync::Mutex::new(DbHandle(this.app_state.db.clone()))),
peer: this.peer.clone(),
connection_pool: this.connection_pool.clone(),
live_kit_client: this.app_state.live_kit_client.clone()
@@ -409,6 +498,7 @@ impl Server {
let next_message = incoming_rx.next().fuse();
futures::pin_mut!(next_message);
futures::select_biased! {
+ _ = teardown.changed().fuse() => return Ok(()),
result = handle_io => {
if let Err(error) = result {
tracing::error!(?error, %user_id, %login, %connection_id, %address, "error handling I/O");
@@ -460,7 +550,7 @@ impl Server {
) -> Result<()> {
if let Some(user) = self.app_state.db.get_user_by_id(inviter_id).await? {
if let Some(code) = &user.invite_code {
- let pool = self.connection_pool.lock().await;
+ let pool = self.connection_pool.lock();
let invitee_contact = contact_for_user(invitee_id, true, false, &pool);
for connection_id in pool.user_connection_ids(inviter_id) {
self.peer.send(
@@ -486,7 +576,7 @@ impl Server {
pub async fn invite_count_updated(self: &Arc<Self>, user_id: UserId) -> Result<()> {
if let Some(user) = self.app_state.db.get_user_by_id(user_id).await? {
if let Some(invite_code) = &user.invite_code {
- let pool = self.connection_pool.lock().await;
+ let pool = self.connection_pool.lock();
for connection_id in pool.user_connection_ids(user_id) {
self.peer.send(
connection_id,
@@ -507,7 +597,7 @@ impl Server {
pub async fn snapshot<'a>(self: &'a Arc<Self>) -> ServerSnapshot<'a> {
ServerSnapshot {
connection_pool: ConnectionPoolGuard {
- guard: self.connection_pool.lock().await,
+ guard: self.connection_pool.lock(),
_not_send: PhantomData,
},
peer: &self.peer,
@@ -628,7 +718,6 @@ pub async fn handle_metrics(Extension(server): Extension<Arc<Server>>) -> Result
let connections = server
.connection_pool
.lock()
- .await
.connections()
.filter(|connection| !connection.admin)
.count();
@@ -681,7 +770,7 @@ async fn sign_out(
{
let db = session.db().await;
if let Some(room) = db.decline_call(None, session.user_id).await.trace_err() {
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
}
}
update_user_contacts(session.user_id, &session).await?;
@@ -749,17 +838,14 @@ async fn join_room(
response: Response<proto::JoinRoom>,
session: Session,
) -> Result<()> {
+ let room_id = RoomId::from_proto(request.id);
let room = {
let room = session
.db()
.await
- .join_room(
- RoomId::from_proto(request.id),
- session.user_id,
- session.connection_id,
- )
+ .join_room(room_id, session.user_id, session.connection_id)
.await?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
room.clone()
};
@@ -770,7 +856,12 @@ async fn join_room(
{
session
.peer
- .send(connection_id, proto::CallCanceled {})
+ .send(
+ connection_id,
+ proto::CallCanceled {
+ room_id: room_id.to_proto(),
+ },
+ )
.trace_err();
}
@@ -834,7 +925,7 @@ async fn call(
initial_project_id,
)
.await?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
mem::take(incoming_call)
};
update_user_contacts(called_user_id, &session).await?;
@@ -864,7 +955,7 @@ async fn call(
.await
.call_failed(room_id, called_user_id)
.await?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
}
update_user_contacts(called_user_id, &session).await?;
@@ -884,7 +975,7 @@ async fn cancel_call(
.await
.cancel_call(Some(room_id), session.connection_id, called_user_id)
.await?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
}
for connection_id in session
@@ -894,7 +985,12 @@ async fn cancel_call(
{
session
.peer
- .send(connection_id, proto::CallCanceled {})
+ .send(
+ connection_id,
+ proto::CallCanceled {
+ room_id: room_id.to_proto(),
+ },
+ )
.trace_err();
}
response.send(proto::Ack {})?;
@@ -911,7 +1007,7 @@ async fn decline_call(message: proto::DeclineCall, session: Session) -> Result<(
.await
.decline_call(Some(room_id), session.user_id)
.await?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
}
for connection_id in session
@@ -921,7 +1017,12 @@ async fn decline_call(message: proto::DeclineCall, session: Session) -> Result<(
{
session
.peer
- .send(connection_id, proto::CallCanceled {})
+ .send(
+ connection_id,
+ proto::CallCanceled {
+ room_id: room_id.to_proto(),
+ },
+ )
.trace_err();
}
update_user_contacts(session.user_id, &session).await?;
@@ -942,7 +1043,7 @@ async fn update_participant_location(
.await
.update_room_participant_location(room_id, session.connection_id, location)
.await?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
response.send(proto::Ack {})?;
Ok(())
}
@@ -964,7 +1065,7 @@ async fn share_project(
response.send(proto::ShareProjectResponse {
project_id: project_id.to_proto(),
})?;
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
Ok(())
}
@@ -983,7 +1084,7 @@ async fn unshare_project(message: proto::UnshareProject, session: Session) -> Re
guest_connection_ids.iter().copied(),
|conn_id| session.peer.send(conn_id, message.clone()),
);
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
Ok(())
}
@@ -1142,7 +1243,7 @@ async fn update_project(
.forward_send(session.connection_id, connection_id, request.clone())
},
);
- room_updated(&room, &session);
+ room_updated(&room, &session.peer);
response.send(proto::Ack {})?;
Ok(())
@@ -1789,17 +1890,15 @@ fn contact_for_user(
}
}
-fn room_updated(room: &proto::Room, session: &Session) {
+fn room_updated(room: &proto::Room, peer: &Peer) {
for participant in &room.participants {
- session
- .peer
- .send(
- ConnectionId(participant.peer_id),
- proto::RoomUpdated {
- room: Some(room.clone()),
- },
- )
- .trace_err();
+ peer.send(
+ ConnectionId(participant.peer_id),
+ proto::RoomUpdated {
+ room: Some(room.clone()),
+ },
+ )
+ .trace_err();
}
}
@@ -1840,6 +1939,7 @@ async fn update_user_contacts(user_id: UserId, session: &Session) -> Result<()>
async fn leave_room_for_session(session: &Session) -> Result<()> {
let mut contacts_to_update = HashSet::default();
+ let room_id;
let canceled_calls_to_user_ids;
let live_kit_room;
let delete_live_kit_room;
@@ -1851,7 +1951,8 @@ async fn leave_room_for_session(session: &Session) -> Result<()> {
project_left(project, session);
}
- room_updated(&left_room.room, &session);
+ room_updated(&left_room.room, &session.peer);
+ room_id = RoomId::from_proto(left_room.room.id);
canceled_calls_to_user_ids = mem::take(&mut left_room.canceled_calls_to_user_ids);
live_kit_room = mem::take(&mut left_room.room.live_kit_room);
delete_live_kit_room = left_room.room.participants.is_empty();
@@ -1863,7 +1964,12 @@ async fn leave_room_for_session(session: &Session) -> Result<()> {
for connection_id in pool.user_connection_ids(canceled_user_id) {
session
.peer
- .send(connection_id, proto::CallCanceled {})
+ .send(
+ connection_id,
+ proto::CallCanceled {
+ room_id: room_id.to_proto(),
+ },
+ )
.trace_err();
}
contacts_to_update.insert(canceled_user_id);