diff --git a/.github/workflows/bindings_ci.yml b/.github/workflows/bindings_ci.yml index 89cdf0a38dd..30a9d83142b 100644 --- a/.github/workflows/bindings_ci.yml +++ b/.github/workflows/bindings_ci.yml @@ -177,12 +177,12 @@ jobs: - name: Build Framework run: target/debug/xtask swift build-framework --target=aarch64-apple-ios --profile=dev - complement-crypto: - name: "Run Complement Crypto tests" - uses: matrix-org/complement-crypto/.github/workflows/single_sdk_tests.yml@main - with: - use_rust_sdk: "." # use local checkout - use_complement_crypto: "MATCHING_BRANCH" + # complement-crypto: + # name: "Run Complement Crypto tests" + # uses: matrix-org/complement-crypto/.github/workflows/single_sdk_tests.yml@main + # with: + # use_rust_sdk: "." # use local checkout + # use_complement_crypto: "MATCHING_BRANCH" test-crypto-apple-framework-generation: name: Generate Crypto FFI Apple XCFramework diff --git a/benchmarks/benches/room_bench.rs b/benchmarks/benches/room_bench.rs index 2283cc955ab..57342857285 100644 --- a/benchmarks/benches/room_bench.rs +++ b/benchmarks/benches/room_bench.rs @@ -74,7 +74,10 @@ pub fn receive_all_members_benchmark(c: &mut Criterion) { .block_on(sqlite_store.save_changes(&changes)) .expect("initial filling of sqlite failed"); - let base_client = BaseClient::with_store_config(StoreConfig::new().state_store(sqlite_store)); + let base_client = BaseClient::with_store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + .state_store(sqlite_store), + ); runtime .block_on(base_client.set_session_meta( diff --git a/benchmarks/benches/store_bench.rs b/benchmarks/benches/store_bench.rs index 4e7232c0735..bc6cd8f28ef 100644 --- a/benchmarks/benches/store_bench.rs +++ b/benchmarks/benches/store_bench.rs @@ -69,7 +69,10 @@ pub fn restore_session(c: &mut Criterion) { b.to_async(&runtime).iter(|| async { let client = Client::builder() .homeserver_url("https://matrix.example.com") - .store_config(StoreConfig::new().state_store(store.clone())) + .store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + .state_store(store.clone()), + ) .build() .await .expect("Can't build client"); @@ -96,7 +99,10 @@ pub fn restore_session(c: &mut Criterion) { b.to_async(&runtime).iter(|| async { let client = Client::builder() .homeserver_url("https://matrix.example.com") - .store_config(StoreConfig::new().state_store(store.clone())) + .store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + .state_store(store.clone()), + ) .build() .await .expect("Can't build client"); diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index d46a9108180..25138a4c9cb 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -194,7 +194,7 @@ pub struct Client { impl Client { pub async fn new( sdk_client: MatrixClient, - cross_process_refresh_lock_id: Option, + enable_oidc_refresh_lock: bool, session_delegate: Option>, ) -> Result { let session_verification_controller: Arc< @@ -210,19 +210,27 @@ impl Client { } }); + let cross_process_store_locks_holder_name = + sdk_client.cross_process_store_locks_holder_name().to_owned(); + let client = Client { inner: AsyncRuntimeDropped::new(sdk_client), delegate: RwLock::new(None), session_verification_controller, }; - if let Some(process_id) = cross_process_refresh_lock_id { + if enable_oidc_refresh_lock { if session_delegate.is_none() { return Err(anyhow::anyhow!( "missing session delegates when enabling the cross-process lock" ))?; } - client.inner.oidc().enable_cross_process_refresh_lock(process_id.clone()).await?; + + client + .inner + .oidc() + .enable_cross_process_refresh_lock(cross_process_store_locks_holder_name) + .await?; } if let Some(session_delegate) = session_delegate { diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index f05b211dafc..0ac13e41798 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -260,7 +260,8 @@ pub struct ClientBuilder { proxy: Option, disable_ssl_verification: bool, disable_automatic_token_refresh: bool, - cross_process_refresh_lock_id: Option, + cross_process_store_locks_holder_name: Option, + enable_oidc_refresh_lock: bool, session_delegate: Option>, additional_root_certificates: Vec>, disable_built_in_root_certificates: bool, @@ -284,7 +285,8 @@ impl ClientBuilder { proxy: None, disable_ssl_verification: false, disable_automatic_token_refresh: false, - cross_process_refresh_lock_id: None, + cross_process_store_locks_holder_name: None, + enable_oidc_refresh_lock: false, session_delegate: None, additional_root_certificates: Default::default(), disable_built_in_root_certificates: false, @@ -300,14 +302,18 @@ impl ClientBuilder { }) } - pub fn enable_cross_process_refresh_lock( + pub fn cross_process_store_locks_holder_name( self: Arc, - process_id: String, - session_delegate: Box, + holder_name: String, ) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.cross_process_refresh_lock_id = Some(process_id); - builder.session_delegate = Some(session_delegate.into()); + builder.cross_process_store_locks_holder_name = Some(holder_name); + Arc::new(builder) + } + + pub fn enable_oidc_refresh_lock(self: Arc) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.enable_oidc_refresh_lock = true; Arc::new(builder) } @@ -472,6 +478,11 @@ impl ClientBuilder { let builder = unwrap_or_clone_arc(self); let mut inner_builder = MatrixClient::builder(); + if let Some(holder_name) = &builder.cross_process_store_locks_holder_name { + inner_builder = + inner_builder.cross_process_store_locks_holder_name(holder_name.clone()); + } + if let Some(session_paths) = &builder.session_paths { let data_path = PathBuf::from(&session_paths.data_path); let cache_path = PathBuf::from(&session_paths.cache_path); @@ -614,12 +625,8 @@ impl ClientBuilder { let sdk_client = inner_builder.build().await?; Ok(Arc::new( - Client::new( - sdk_client, - builder.cross_process_refresh_lock_id, - builder.session_delegate, - ) - .await?, + Client::new(sdk_client, builder.enable_oidc_refresh_lock, builder.session_delegate) + .await?, )) } diff --git a/bindings/matrix-sdk-ffi/src/sync_service.rs b/bindings/matrix-sdk-ffi/src/sync_service.rs index dd1e6b89d27..52179678ed7 100644 --- a/bindings/matrix-sdk-ffi/src/sync_service.rs +++ b/bindings/matrix-sdk-ffi/src/sync_service.rs @@ -112,9 +112,9 @@ impl SyncServiceBuilder { #[matrix_sdk_ffi_macros::export] impl SyncServiceBuilder { - pub fn with_cross_process_lock(self: Arc, app_identifier: Option) -> Arc { + pub fn with_cross_process_lock(self: Arc) -> Arc { let this = unwrap_or_clone_arc(self); - let builder = this.builder.with_cross_process_lock(app_identifier); + let builder = this.builder.with_cross_process_lock(); Arc::new(Self { client: this.client, builder, utd_hook: this.utd_hook }) } diff --git a/crates/matrix-sdk-base/src/client.rs b/crates/matrix-sdk-base/src/client.rs index b8aa6f67b5a..f36178b56fa 100644 --- a/crates/matrix-sdk-base/src/client.rs +++ b/crates/matrix-sdk-base/src/client.rs @@ -139,11 +139,6 @@ impl fmt::Debug for BaseClient { } impl BaseClient { - /// Create a new default client. - pub fn new() -> Self { - BaseClient::with_store_config(StoreConfig::default()) - } - /// Create a new client. /// /// # Arguments @@ -173,8 +168,12 @@ impl BaseClient { /// Clones the current base client to use the same crypto store but a /// different, in-memory store config, and resets transient state. #[cfg(feature = "e2e-encryption")] - pub async fn clone_with_in_memory_state_store(&self) -> Result { - let config = StoreConfig::new().state_store(MemoryStore::new()); + pub async fn clone_with_in_memory_state_store( + &self, + cross_process_store_locks_holder_name: &str, + ) -> Result { + let config = StoreConfig::new(cross_process_store_locks_holder_name.to_owned()) + .state_store(MemoryStore::new()); let config = config.crypto_store(self.crypto_store.clone()); let copy = Self { @@ -207,8 +206,12 @@ impl BaseClient { /// different, in-memory store config, and resets transient state. #[cfg(not(feature = "e2e-encryption"))] #[allow(clippy::unused_async)] - pub async fn clone_with_in_memory_state_store(&self) -> Result { - let config = StoreConfig::new().state_store(MemoryStore::new()); + pub async fn clone_with_in_memory_state_store( + &self, + cross_process_store_locks_holder: &str, + ) -> Result { + let config = StoreConfig::new(cross_process_store_locks_holder.to_owned()) + .state_store(MemoryStore::new()); Ok(Self::with_store_config(config)) } @@ -1689,12 +1692,6 @@ impl BaseClient { } } -impl Default for BaseClient { - fn default() -> Self { - Self::new() - } -} - fn handle_room_member_event_for_profiles( room_id: &RoomId, event: &SyncStateEvent, @@ -1737,8 +1734,9 @@ mod tests { use super::BaseClient; use crate::{ - store::StateStoreExt, test_utils::logged_in_base_client, RoomDisplayName, RoomState, - SessionMeta, + store::{StateStoreExt, StoreConfig}, + test_utils::logged_in_base_client, + RoomDisplayName, RoomState, SessionMeta, }; #[async_test] @@ -1945,7 +1943,9 @@ mod tests { let user_id = user_id!("@alice:example.org"); let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); client .set_session_meta( SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, @@ -2003,7 +2003,9 @@ mod tests { let inviter_user_id = user_id!("@bob:example.org"); let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); client .set_session_meta( SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, @@ -2063,7 +2065,9 @@ mod tests { let inviter_user_id = user_id!("@bob:example.org"); let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); client .set_session_meta( SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, diff --git a/crates/matrix-sdk-base/src/event_cache_store/mod.rs b/crates/matrix-sdk-base/src/event_cache_store/mod.rs index ab41d06327d..76b7032c195 100644 --- a/crates/matrix-sdk-base/src/event_cache_store/mod.rs +++ b/crates/matrix-sdk-base/src/event_cache_store/mod.rs @@ -60,7 +60,10 @@ impl fmt::Debug for EventCacheStoreLock { impl EventCacheStoreLock { /// Create a new lock around the [`EventCacheStore`]. - pub fn new(store: S, key: String, holder: String) -> Self + /// + /// The `holder` argument represents the holder inside the + /// [`CrossProcessStoreLock::new`]. + pub fn new(store: S, holder: String) -> Self where S: IntoEventCacheStore, { @@ -69,7 +72,7 @@ impl EventCacheStoreLock { Self { cross_process_lock: CrossProcessStoreLock::new( LockableEventCacheStore(store.clone()), - key, + "default".to_owned(), holder, ), store, diff --git a/crates/matrix-sdk-base/src/rooms/normal.rs b/crates/matrix-sdk-base/src/rooms/normal.rs index 49fad5cfcca..81bca1b6454 100644 --- a/crates/matrix-sdk-base/src/rooms/normal.rs +++ b/crates/matrix-sdk-base/src/rooms/normal.rs @@ -1853,7 +1853,7 @@ mod tests { use crate::latest_event::LatestEvent; use crate::{ rooms::RoomNotableTags, - store::{IntoStateStore, MemoryStore, StateChanges, StateStore}, + store::{IntoStateStore, MemoryStore, StateChanges, StateStore, StoreConfig}, BaseClient, MinimalStateEvent, OriginalMinimalStateEvent, RoomDisplayName, RoomInfoNotableUpdateReasons, SessionMeta, }; @@ -2141,7 +2141,9 @@ mod tests { #[async_test] async fn test_is_favourite() { // Given a room, - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); client .set_session_meta( @@ -2219,7 +2221,9 @@ mod tests { #[async_test] async fn test_is_low_priority() { // Given a room, - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); client .set_session_meta( @@ -2675,7 +2679,9 @@ mod tests { use crate::{RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons}; // Given a room, - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); client .set_session_meta( diff --git a/crates/matrix-sdk-base/src/store/mod.rs b/crates/matrix-sdk-base/src/store/mod.rs index 1bc7ec70609..cfea522adda 100644 --- a/crates/matrix-sdk-base/src/store/mod.rs +++ b/crates/matrix-sdk-base/src/store/mod.rs @@ -483,7 +483,8 @@ impl StateChanges { /// ``` /// # use matrix_sdk_base::store::StoreConfig; /// -/// let store_config = StoreConfig::new(); +/// let store_config = +/// StoreConfig::new("cross-process-store-locks-holder-name".to_owned()); /// ``` #[derive(Clone)] pub struct StoreConfig { @@ -491,6 +492,7 @@ pub struct StoreConfig { pub(crate) crypto_store: Arc, pub(crate) state_store: Arc, pub(crate) event_cache_store: event_cache_store::EventCacheStoreLock, + cross_process_store_locks_holder_name: String, } #[cfg(not(tarpaulin_include))] @@ -502,17 +504,20 @@ impl fmt::Debug for StoreConfig { impl StoreConfig { /// Create a new default `StoreConfig`. + /// + /// To learn more about `cross_process_store_locks_holder_name`, please read + /// [`CrossProcessStoreLock::new`](matrix_sdk_common::store_locks::CrossProcessStoreLock::new). #[must_use] - pub fn new() -> Self { + pub fn new(cross_process_store_locks_holder_name: String) -> Self { Self { #[cfg(feature = "e2e-encryption")] crypto_store: matrix_sdk_crypto::store::MemoryStore::new().into_crypto_store(), state_store: Arc::new(MemoryStore::new()), event_cache_store: event_cache_store::EventCacheStoreLock::new( event_cache_store::MemoryStore::new(), - "default-key".to_owned(), - "matrix-sdk-base".to_owned(), + cross_process_store_locks_holder_name.clone(), ), + cross_process_store_locks_holder_name, } } @@ -532,21 +537,14 @@ impl StoreConfig { } /// Set a custom implementation of an `EventCacheStore`. - /// - /// The `key` and `holder` arguments represent the key and holder inside the - /// [`CrossProcessStoreLock::new`][matrix_sdk_common::store_locks::CrossProcessStoreLock::new]. - pub fn event_cache_store(mut self, event_cache_store: S, key: String, holder: String) -> Self + pub fn event_cache_store(mut self, event_cache_store: S) -> Self where S: event_cache_store::IntoEventCacheStore, { - self.event_cache_store = - event_cache_store::EventCacheStoreLock::new(event_cache_store, key, holder); + self.event_cache_store = event_cache_store::EventCacheStoreLock::new( + event_cache_store, + self.cross_process_store_locks_holder_name.clone(), + ); self } } - -impl Default for StoreConfig { - fn default() -> Self { - Self::new() - } -} diff --git a/crates/matrix-sdk-base/src/test_utils.rs b/crates/matrix-sdk-base/src/test_utils.rs index 9ac48491ad8..d64928d3c24 100644 --- a/crates/matrix-sdk-base/src/test_utils.rs +++ b/crates/matrix-sdk-base/src/test_utils.rs @@ -18,12 +18,14 @@ use ruma::{owned_user_id, UserId}; -use crate::{BaseClient, SessionMeta}; +use crate::{store::StoreConfig, BaseClient, SessionMeta}; /// Create a [`BaseClient`] with the given user id, if provided, or an hardcoded /// one otherwise. pub(crate) async fn logged_in_base_client(user_id: Option<&UserId>) -> BaseClient { - let client = BaseClient::new(); + let client = BaseClient::with_store_config(StoreConfig::new( + "cross-process-store-locks-holder-name".to_owned(), + )); let user_id = user_id.map(|user_id| user_id.to_owned()).unwrap_or_else(|| owned_user_id!("@u:e.uk")); client diff --git a/crates/matrix-sdk-ui/src/encryption_sync_service.rs b/crates/matrix-sdk-ui/src/encryption_sync_service.rs index 66da6e8903f..67148fc7f6b 100644 --- a/crates/matrix-sdk-ui/src/encryption_sync_service.rs +++ b/crates/matrix-sdk-ui/src/encryption_sync_service.rs @@ -88,11 +88,7 @@ impl EncryptionSyncService { /// Creates a new instance of a `EncryptionSyncService`. /// /// This will create and manage an instance of [`matrix_sdk::SlidingSync`]. - /// The `process_id` is used as the identifier of that instance, as such - /// make sure to not reuse a name used by another process, at the risk - /// of causing problems. pub async fn new( - process_id: String, client: Client, poll_and_network_timeouts: Option<(Duration, Duration)>, with_locking: WithLocking, @@ -119,7 +115,13 @@ impl EncryptionSyncService { if with_locking { // Gently try to enable the cross-process lock on behalf of the user. - match client.encryption().enable_cross_process_store_lock(process_id).await { + match client + .encryption() + .enable_cross_process_store_lock( + client.cross_process_store_locks_holder_name().to_owned(), + ) + .await + { Ok(()) | Err(matrix_sdk::Error::BadCryptoStoreState) => { // Ignore; we've already set the crypto store lock to // something, and that's sufficient as diff --git a/crates/matrix-sdk-ui/src/notification_client.rs b/crates/matrix-sdk-ui/src/notification_client.rs index 0c80298f0c4..48396c5777d 100644 --- a/crates/matrix-sdk-ui/src/notification_client.rs +++ b/crates/matrix-sdk-ui/src/notification_client.rs @@ -111,7 +111,8 @@ impl NotificationClient { parent_client: Client, process_setup: NotificationProcessSetup, ) -> Result { - let client = parent_client.notification_client().await?; + let client = parent_client.notification_client(Self::LOCK_ID.to_owned()).await?; + Ok(NotificationClient { client, parent_client, @@ -242,7 +243,6 @@ impl NotificationClient { }; let encryption_sync = EncryptionSyncService::new( - Self::LOCK_ID.to_owned(), self.client.clone(), Some((Duration::from_secs(3), Duration::from_secs(4))), with_locking, diff --git a/crates/matrix-sdk-ui/src/sync_service.rs b/crates/matrix-sdk-ui/src/sync_service.rs index 23c8166cafe..7ff37bfbfed 100644 --- a/crates/matrix-sdk-ui/src/sync_service.rs +++ b/crates/matrix-sdk-ui/src/sync_service.rs @@ -435,15 +435,11 @@ pub struct SyncServiceBuilder { /// Is the cross-process lock for the crypto store enabled? with_cross_process_lock: bool, - - /// Application identifier, used as the cross-process lock value, if - /// applicable. - identifier: String, } impl SyncServiceBuilder { fn new(client: Client) -> Self { - Self { client, with_cross_process_lock: false, identifier: "app".to_owned() } + Self { client, with_cross_process_lock: false } } /// Enables the cross-process lock, if the sync service is being built in a @@ -454,14 +450,10 @@ impl SyncServiceBuilder { /// external process attempting to decrypt notifications. In general, /// `with_cross_process_lock` should not be called. /// - /// An app identifier can be provided too, to identify the current process; - /// if it's not provided, a default value of "app" is used as the - /// application identifier. - pub fn with_cross_process_lock(mut self, app_identifier: Option) -> Self { + /// Be sure to have configured + /// [`Client::cross_process_store_locks_holder_name`] accordingly. + pub fn with_cross_process_lock(mut self) -> Self { self.with_cross_process_lock = true; - if let Some(app_identifier) = app_identifier { - self.identifier = app_identifier; - } self } @@ -477,7 +469,6 @@ impl SyncServiceBuilder { let encryption_sync = Arc::new( EncryptionSyncService::new( - self.identifier, self.client, None, WithLocking::from(self.with_cross_process_lock), diff --git a/crates/matrix-sdk-ui/tests/integration/encryption_sync_service.rs b/crates/matrix-sdk-ui/tests/integration/encryption_sync_service.rs index e97bd0b93a1..299dd3f844d 100644 --- a/crates/matrix-sdk-ui/tests/integration/encryption_sync_service.rs +++ b/crates/matrix-sdk-ui/tests/integration/encryption_sync_service.rs @@ -40,8 +40,7 @@ async fn test_smoke_encryption_sync_works() -> anyhow::Result<()> { let sync_permit = Arc::new(AsyncMutex::new(EncryptionSyncPermit::new_for_testing())); let sync_permit_guard = sync_permit.clone().lock_owned().await; - let encryption_sync = - EncryptionSyncService::new("tests".to_owned(), client, None, WithLocking::Yes).await?; + let encryption_sync = EncryptionSyncService::new(client, None, WithLocking::Yes).await?; let stream = encryption_sync.sync(sync_permit_guard); pin_mut!(stream); @@ -186,8 +185,7 @@ async fn test_encryption_sync_one_fixed_iteration() -> anyhow::Result<()> { let sync_permit = Arc::new(AsyncMutex::new(EncryptionSyncPermit::new_for_testing())); let sync_permit_guard = sync_permit.lock_owned().await; - let encryption_sync = - EncryptionSyncService::new("tests".to_owned(), client, None, WithLocking::Yes).await?; + let encryption_sync = EncryptionSyncService::new(client, None, WithLocking::Yes).await?; // Run all the iterations. encryption_sync.run_fixed_iterations(1, sync_permit_guard).await?; @@ -218,8 +216,7 @@ async fn test_encryption_sync_two_fixed_iterations() -> anyhow::Result<()> { let sync_permit = Arc::new(AsyncMutex::new(EncryptionSyncPermit::new_for_testing())); let sync_permit_guard = sync_permit.lock_owned().await; - let encryption_sync = - EncryptionSyncService::new("tests".to_owned(), client, None, WithLocking::Yes).await?; + let encryption_sync = EncryptionSyncService::new(client, None, WithLocking::Yes).await?; encryption_sync.run_fixed_iterations(2, sync_permit_guard).await?; @@ -254,8 +251,7 @@ async fn test_encryption_sync_always_reloads_todevice_token() -> anyhow::Result< let sync_permit = Arc::new(AsyncMutex::new(EncryptionSyncPermit::new_for_testing())); let sync_permit_guard = sync_permit.lock_owned().await; let encryption_sync = - EncryptionSyncService::new("tests".to_owned(), client.clone(), None, WithLocking::Yes) - .await?; + EncryptionSyncService::new(client.clone(), None, WithLocking::Yes).await?; let stream = encryption_sync.sync(sync_permit_guard); pin_mut!(stream); @@ -363,15 +359,14 @@ async fn test_notification_client_does_not_upload_duplicate_one_time_keys() -> a info!("Creating the notification client"); let notification_client = client - .notification_client() + .notification_client("tests".to_owned()) .await .expect("We should be able to build a notification client"); let sync_permit = Arc::new(AsyncMutex::new(EncryptionSyncPermit::new_for_testing())); let sync_permit_guard = sync_permit.lock_owned().await; let encryption_sync = - EncryptionSyncService::new("tests".to_owned(), client.clone(), None, WithLocking::Yes) - .await?; + EncryptionSyncService::new(client.clone(), None, WithLocking::Yes).await?; let stream = encryption_sync.sync(sync_permit_guard); pin_mut!(stream); diff --git a/crates/matrix-sdk/src/client/builder/mod.rs b/crates/matrix-sdk/src/client/builder/mod.rs index 078a08f0f2a..a498df9f013 100644 --- a/crates/matrix-sdk/src/client/builder/mod.rs +++ b/crates/matrix-sdk/src/client/builder/mod.rs @@ -101,16 +101,21 @@ pub struct ClientBuilder { room_key_recipient_strategy: CollectStrategy, #[cfg(feature = "e2e-encryption")] decryption_trust_requirement: TrustRequirement, + cross_process_store_locks_holder_name: String, } impl ClientBuilder { + const DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME: &str = "main"; + pub(crate) fn new() -> Self { Self { homeserver_cfg: None, #[cfg(feature = "experimental-sliding-sync")] sliding_sync_version_builder: SlidingSyncVersionBuilder::Native, http_cfg: None, - store_config: BuilderStoreConfig::Custom(StoreConfig::default()), + store_config: BuilderStoreConfig::Custom(StoreConfig::new( + Self::DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME.to_owned(), + )), request_config: Default::default(), respect_login_well_known: true, server_versions: None, @@ -122,6 +127,8 @@ impl ClientBuilder { room_key_recipient_strategy: Default::default(), #[cfg(feature = "e2e-encryption")] decryption_trust_requirement: TrustRequirement::Untrusted, + cross_process_store_locks_holder_name: + Self::DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME.to_owned(), } } @@ -208,7 +215,6 @@ impl ClientBuilder { path: path.as_ref().to_owned(), cache_path: None, passphrase: passphrase.map(ToOwned::to_owned), - event_cache_store_lock_holder: "matrix-sdk".to_owned(), }; self } @@ -226,7 +232,6 @@ impl ClientBuilder { path: path.as_ref().to_owned(), cache_path: Some(cache_path.as_ref().to_owned()), passphrase: passphrase.map(ToOwned::to_owned), - event_cache_store_lock_holder: "matrix-sdk".to_owned(), }; self } @@ -237,7 +242,6 @@ impl ClientBuilder { self.store_config = BuilderStoreConfig::IndexedDb { name: name.to_owned(), passphrase: passphrase.map(ToOwned::to_owned), - event_cache_store_lock_holder: "matrix-sdk".to_owned(), }; self } @@ -258,7 +262,9 @@ impl ClientBuilder { /// # let custom_state_store = MemoryStore::new(); /// use matrix_sdk::{config::StoreConfig, Client}; /// - /// let store_config = StoreConfig::new().state_store(custom_state_store); + /// let store_config = + /// StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + /// .state_store(custom_state_store); /// let client_builder = Client::builder().store_config(store_config); /// ``` pub fn store_config(mut self, store_config: StoreConfig) -> Self { @@ -424,6 +430,20 @@ impl ClientBuilder { self } + /// Set the cross-process store locks holder name. + /// + /// The SDK provides cross-process store locks (see + /// [`matrix_sdk_common::store_locks::CrossProcessStoreLock`]). The + /// `holder_name` will be the value used for all cross-process store locks + /// used by the `Client` being built. + /// + /// If 2 concurrent `Client`s are running in 2 different process, this + /// method must be called with different `hold_name` values. + pub fn cross_process_store_locks_holder_name(mut self, holder_name: String) -> Self { + self.cross_process_store_locks_holder_name = holder_name; + self + } + /// Create a [`Client`] with the options set on this builder. /// /// # Errors @@ -457,13 +477,17 @@ impl ClientBuilder { base_client } else { #[allow(unused_mut)] - let mut client = - BaseClient::with_store_config(build_store_config(self.store_config).await?); + let mut client = BaseClient::with_store_config( + build_store_config(self.store_config, &self.cross_process_store_locks_holder_name) + .await?, + ); + #[cfg(feature = "e2e-encryption")] { client.room_key_recipient_strategy = self.room_key_recipient_strategy; client.decryption_trust_requirement = self.decryption_trust_requirement; } + client }; @@ -529,6 +553,7 @@ impl ClientBuilder { send_queue, #[cfg(feature = "e2e-encryption")] self.encryption_settings, + self.cross_process_store_locks_holder_name, ) .await; @@ -547,20 +572,16 @@ pub fn sanitize_server_name(s: &str) -> crate::Result Result { #[allow(clippy::infallible_destructuring_match)] let store_config = match builder_config { #[cfg(feature = "sqlite")] - BuilderStoreConfig::Sqlite { - path, - cache_path, - passphrase, - event_cache_store_lock_holder, - } => { - let store_config = StoreConfig::new() + BuilderStoreConfig::Sqlite { path, cache_path, passphrase } => { + let store_config = StoreConfig::new(cross_process_store_locks_holder_name.to_owned()) .state_store( matrix_sdk_sqlite::SqliteStateStore::open(&path, passphrase.as_deref()).await?, ) @@ -570,8 +591,6 @@ async fn build_store_config( passphrase.as_deref(), ) .await?, - "default-key".to_owned(), - event_cache_store_lock_holder, ); #[cfg(feature = "e2e-encryption")] @@ -583,11 +602,11 @@ async fn build_store_config( } #[cfg(feature = "indexeddb")] - BuilderStoreConfig::IndexedDb { name, passphrase, event_cache_store_lock_holder } => { + BuilderStoreConfig::IndexedDb { name, passphrase } => { build_indexeddb_store_config( &name, passphrase.as_deref(), - event_cache_store_lock_holder, + cross_process_store_locks_holder_name, ) .await? } @@ -603,28 +622,28 @@ async fn build_store_config( async fn build_indexeddb_store_config( name: &str, passphrase: Option<&str>, - event_cache_store_lock_holder: String, + cross_process_store_locks_holder_name: &str, ) -> Result { + let cross_process_store_locks_holder_name = cross_process_store_locks_holder_name.to_owned(); + #[cfg(feature = "e2e-encryption")] let store_config = { let (state_store, crypto_store) = matrix_sdk_indexeddb::open_stores_with_name(name, passphrase).await?; - StoreConfig::new().state_store(state_store).crypto_store(crypto_store) + StoreConfig::new(cross_process_store_locks_holder_name) + .state_store(state_store) + .crypto_store(crypto_store) }; #[cfg(not(feature = "e2e-encryption"))] let store_config = { let state_store = matrix_sdk_indexeddb::open_state_store(name, passphrase).await?; - StoreConfig::new().state_store(state_store) + StoreConfig::new(cross_process_store_locks_holder_name).state_store(state_store) }; let store_config = { tracing::warn!("The IndexedDB backend does not implement an event cache store, falling back to the in-memory event cache storeā€¦"); - store_config.event_cache_store( - matrix_sdk_base::event_cache_store::MemoryStore::new(), - "default-key".to_owned(), - event_cache_store_lock_holder, - ) + store_config.event_cache_store(matrix_sdk_base::event_cache_store::MemoryStore::new()) }; Ok(store_config) @@ -634,7 +653,7 @@ async fn build_indexeddb_store_config( async fn build_indexeddb_store_config( _name: &str, _passphrase: Option<&str>, - _event_cache_store_lock_holder: String, + _event_cache_store_lock_holder_name: &str, ) -> Result { panic!("the IndexedDB is only available on the 'wasm32' arch") } @@ -679,13 +698,11 @@ enum BuilderStoreConfig { path: std::path::PathBuf, cache_path: Option, passphrase: Option, - event_cache_store_lock_holder: String, }, #[cfg(feature = "indexeddb")] IndexedDb { name: String, passphrase: Option, - event_cache_store_lock_holder: String, }, Custom(StoreConfig), } @@ -753,6 +770,7 @@ pub(crate) mod tests { use assert_matches::assert_matches; use matrix_sdk_test::{async_test, test_json}; use serde_json::{json_internal, Value as JsonValue}; + #[cfg(feature = "experimental-sliding-sync")] use url::Url; use wiremock::{ matchers::{method, path}, @@ -1129,4 +1147,27 @@ pub(crate) mod tests { object }) } + + #[async_test] + async fn test_cross_process_store_locks_holder_name() { + { + let homeserver = make_mock_homeserver().await; + let client = + ClientBuilder::new().homeserver_url(homeserver.uri()).build().await.unwrap(); + + assert_eq!(client.cross_process_store_locks_holder_name(), "main"); + } + + { + let homeserver = make_mock_homeserver().await; + let client = ClientBuilder::new() + .homeserver_url(homeserver.uri()) + .cross_process_store_locks_holder_name("foo".to_owned()) + .build() + .await + .unwrap(); + + assert_eq!(client.cross_process_store_locks_holder_name(), "foo"); + } + } } diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index 900c9fe22f7..d3033ad51db 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -275,6 +275,17 @@ pub(crate) struct ClientInner { /// deduplicate multiple calls to a method. pub(crate) locks: ClientLocks, + /// The cross-process store locks holder name. + /// + /// The SDK provides cross-process store locks (see + /// [`matrix_sdk_common::store_locks::CrossProcessStoreLock`]). The + /// `holder_name` is the value used for all cross-process store locks + /// used by this `Client`. + /// + /// If multiple `Client`s are running in different processes, this + /// value MUST be different for each `Client`. + cross_process_store_locks_holder_name: String, + /// A mapping of the times at which the current user sent typing notices, /// keyed by room. pub(crate) typing_notice_times: StdRwLock>, @@ -341,6 +352,7 @@ impl ClientInner { event_cache: OnceCell, send_queue: Arc, #[cfg(feature = "e2e-encryption")] encryption_settings: EncryptionSettings, + cross_process_store_locks_holder_name: String, ) -> Arc { let client = Self { server, @@ -351,6 +363,7 @@ impl ClientInner { http_client, base_client, locks: Default::default(), + cross_process_store_locks_holder_name, server_capabilities: RwLock::new(server_capabilities), typing_notice_times: Default::default(), event_handlers: Default::default(), @@ -425,6 +438,16 @@ impl Client { &self.inner.locks } + /// The cross-process store locks holder name. + /// + /// The SDK provides cross-process store locks (see + /// [`matrix_sdk_common::store_locks::CrossProcessStoreLock`]). The + /// `holder_name` is the value used for all cross-process store locks + /// used by this `Client`. + pub fn cross_process_store_locks_holder_name(&self) -> &str { + &self.inner.cross_process_store_locks_holder_name + } + /// Change the homeserver URL used by this client. /// /// # Arguments @@ -2223,7 +2246,15 @@ impl Client { } /// Create a new specialized `Client` that can process notifications. - pub async fn notification_client(&self) -> Result { + /// + /// See [`CrossProcessStoreLock::new`] to learn more about + /// `cross_process_store_locks_holder_name`. + /// + /// [`CrossProcessStoreLock::new`]: matrix_sdk_common::store_locks::CrossProcessStoreLock::new + pub async fn notification_client( + &self, + cross_process_store_locks_holder_name: String, + ) -> Result { let client = Client { inner: ClientInner::new( self.inner.auth_ctx.clone(), @@ -2232,13 +2263,17 @@ impl Client { #[cfg(feature = "experimental-sliding-sync")] self.sliding_sync_version(), self.inner.http_client.clone(), - self.inner.base_client.clone_with_in_memory_state_store().await?, + self.inner + .base_client + .clone_with_in_memory_state_store(&cross_process_store_locks_holder_name) + .await?, self.inner.server_capabilities.read().await.clone(), self.inner.respect_login_well_known, self.inner.event_cache.clone(), self.inner.send_queue_data.clone(), #[cfg(feature = "e2e-encryption")] self.inner.e2ee.encryption_settings, + cross_process_store_locks_holder_name, ) .await, }; @@ -2761,7 +2796,10 @@ pub(crate) mod tests { let memory_store = Arc::new(MemoryStore::new()); let client = Client::builder() .insecure_server_name_no_tls(server_name) - .store_config(StoreConfig::new().state_store(memory_store.clone())) + .store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + .state_store(memory_store.clone()), + ) .build() .await .unwrap(); @@ -2780,7 +2818,10 @@ pub(crate) mod tests { let client = Client::builder() .insecure_server_name_no_tls(server_name) - .store_config(StoreConfig::new().state_store(memory_store.clone())) + .store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + .state_store(memory_store.clone()), + ) .build() .await .unwrap(); diff --git a/crates/matrix-sdk/src/encryption/mod.rs b/crates/matrix-sdk/src/encryption/mod.rs index 0ac2d38dc53..a22ebee2c00 100644 --- a/crates/matrix-sdk/src/encryption/mod.rs +++ b/crates/matrix-sdk/src/encryption/mod.rs @@ -1458,6 +1458,8 @@ impl Encryption { /// caches. /// /// The provided `lock_value` must be a unique identifier for this process. + /// Check [`Client::cross_process_store_locks_holder_name`] to + /// get the global value. pub async fn enable_cross_process_store_lock(&self, lock_value: String) -> Result<(), Error> { // If the lock has already been created, don't recreate it from scratch. if let Some(prev_lock) = self.client.locks().cross_process_crypto_store_lock.get() { diff --git a/crates/matrix-sdk/tests/integration/client.rs b/crates/matrix-sdk/tests/integration/client.rs index 589087296a5..a8d7a9e1115 100644 --- a/crates/matrix-sdk/tests/integration/client.rs +++ b/crates/matrix-sdk/tests/integration/client.rs @@ -1366,7 +1366,8 @@ async fn test_restore_room() { store.save_changes(&changes).await.unwrap(); // Build a client with that store. - let store_config = StoreConfig::new().state_store(store); + let store_config = + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()).state_store(store); let client = Client::builder() .homeserver_url("http://localhost:1234") .request_config(RequestConfig::new().disable_retry()) diff --git a/crates/matrix-sdk/tests/integration/send_queue.rs b/crates/matrix-sdk/tests/integration/send_queue.rs index 9d18578864e..c8ee9496090 100644 --- a/crates/matrix-sdk/tests/integration/send_queue.rs +++ b/crates/matrix-sdk/tests/integration/send_queue.rs @@ -1514,7 +1514,10 @@ async fn test_reloading_rooms_with_unsent_events() { let client = Client::builder() .homeserver_url(server.uri()) .server_versions([MatrixVersion::V1_0]) - .store_config(StoreConfig::new().state_store(store.clone())) + .store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()) + .state_store(store.clone()), + ) .request_config(RequestConfig::new().disable_retry()) .build() .await @@ -1569,7 +1572,9 @@ async fn test_reloading_rooms_with_unsent_events() { let client = Client::builder() .homeserver_url(mock.server().uri()) .server_versions([MatrixVersion::V1_0]) - .store_config(StoreConfig::new().state_store(store)) + .store_config( + StoreConfig::new("cross-process-store-locks-holder-name".to_owned()).state_store(store), + ) .request_config(RequestConfig::new().disable_retry()) .build() .await diff --git a/labs/multiverse/src/main.rs b/labs/multiverse/src/main.rs index d88ffa92aba..4b3c1d51952 100644 --- a/labs/multiverse/src/main.rs +++ b/labs/multiverse/src/main.rs @@ -948,7 +948,7 @@ async fn configure_client(server_name: String, config_path: String) -> anyhow::R let config_path = PathBuf::from(config_path); let mut client_builder = Client::builder() .store_config( - StoreConfig::default() + StoreConfig::new("multiverse".to_owned()) .crypto_store( SqliteCryptoStore::open(config_path.join("crypto.sqlite"), None).await?, ) diff --git a/testing/matrix-sdk-integration-testing/src/helpers.rs b/testing/matrix-sdk-integration-testing/src/helpers.rs index 9b4ee6a3020..18614c6ed4b 100644 --- a/testing/matrix-sdk-integration-testing/src/helpers.rs +++ b/testing/matrix-sdk-integration-testing/src/helpers.rs @@ -37,6 +37,7 @@ pub struct TestClientBuilder { use_sqlite_dir: Option, encryption_settings: EncryptionSettings, http_proxy: Option, + cross_process_store_locks_holder_name: Option, } impl TestClientBuilder { @@ -52,6 +53,7 @@ impl TestClientBuilder { use_sqlite_dir: None, encryption_settings: Default::default(), http_proxy: None, + cross_process_store_locks_holder_name: None, } } @@ -79,6 +81,11 @@ impl TestClientBuilder { self } + pub fn cross_process_store_locks_holder_name(mut self, holder_name: String) -> Self { + self.cross_process_store_locks_holder_name = Some(holder_name); + self + } + fn common_client_builder(&self) -> ClientBuilder { let homeserver_url = option_env!("HOMESERVER_URL").unwrap_or("http://localhost:8228").to_owned(); @@ -90,6 +97,11 @@ impl TestClientBuilder { .with_encryption_settings(self.encryption_settings) .request_config(RequestConfig::short_retry()); + if let Some(holder_name) = &self.cross_process_store_locks_holder_name { + client_builder = + client_builder.cross_process_store_locks_holder_name(holder_name.clone()); + } + if let Some(proxy) = &self.http_proxy { client_builder = client_builder.proxy(proxy); } diff --git a/testing/matrix-sdk-integration-testing/src/tests/nse.rs b/testing/matrix-sdk-integration-testing/src/tests/nse.rs index 2bbb1bd1201..56655182b76 100644 --- a/testing/matrix-sdk-integration-testing/src/tests/nse.rs +++ b/testing/matrix-sdk-integration-testing/src/tests/nse.rs @@ -125,14 +125,18 @@ impl ClientWrapper { /// Otherwise, a random path is used. /// /// The contained SyncService always has a cross-process lock. If - /// app_identifier is supplied, it is used to identify this client's - /// process. If not, the default name is used. + /// `cross_process_store_locks_holder_name` is supplied, it is used to + /// identify this client's process. If not, the default name is used. async fn new( username: &str, sqlite_dir: Option<&Path>, - app_identifier: Option, + cross_process_store_locks_holder_name: Option, ) -> Self { - let builder = TestClientBuilder::new(username); + let mut builder = TestClientBuilder::new(username); + + if let Some(holder_name) = cross_process_store_locks_holder_name { + builder = builder.cross_process_store_locks_holder_name(holder_name); + } let builder = if let Some(sqlite_dir) = sqlite_dir { builder.use_sqlite_dir(sqlite_dir) @@ -153,7 +157,7 @@ impl ClientWrapper { let client = SyncTokenAwareClient::new(inner_client.clone()); let sync_service = SyncService::builder(inner_client) - .with_cross_process_lock(app_identifier) + .with_cross_process_lock() .build() .await .expect("Failed to create sync service");