diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt index 5034dfcbf0e..669a8ae8860 100644 --- a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt +++ b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt @@ -18,7 +18,6 @@ package com.wire.kalium.logic.feature.call -import co.touchlab.stately.collections.ConcurrentMutableMap import com.sun.jna.Pointer import com.wire.kalium.calling.Calling import com.wire.kalium.calling.ENVIRONMENT_DEFAULT @@ -41,13 +40,15 @@ import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.util.CurrentPlatform import com.wire.kalium.logic.util.PlatformContext import com.wire.kalium.logic.util.PlatformType +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap actual class GlobalCallManager( appContext: PlatformContext ) { - private val callManagerHolder: ConcurrentMutableMap by lazy { - ConcurrentMutableMap() + private val callManagerHolder: ConcurrentMap by lazy { + ConcurrentHashMap() } private val calling by lazy { @@ -85,22 +86,22 @@ actual class GlobalCallManager( conversationClientsInCallUpdater: ConversationClientsInCallUpdater, kaliumConfigs: KaliumConfigs ): CallManager { - return callManagerHolder[userId] ?: CallManagerImpl( - calling = calling, - callRepository = callRepository, - userRepository = userRepository, - currentClientIdProvider = currentClientIdProvider, - selfConversationIdProvider = selfConversationIdProvider, - callMapper = callMapper, - messageSender = messageSender, - conversationRepository = conversationRepository, - federatedIdMapper = federatedIdMapper, - qualifiedIdMapper = qualifiedIdMapper, - videoStateChecker = videoStateChecker, - conversationClientsInCallUpdater = conversationClientsInCallUpdater, - kaliumConfigs = kaliumConfigs - ).also { - callManagerHolder[userId] = it + return callManagerHolder.computeIfAbsent(userId) { + CallManagerImpl( + calling = calling, + callRepository = callRepository, + userRepository = userRepository, + currentClientIdProvider = currentClientIdProvider, + selfConversationIdProvider = selfConversationIdProvider, + callMapper = callMapper, + messageSender = messageSender, + conversationRepository = conversationRepository, + federatedIdMapper = federatedIdMapper, + qualifiedIdMapper = qualifiedIdMapper, + videoStateChecker = videoStateChecker, + conversationClientsInCallUpdater = conversationClientsInCallUpdater, + kaliumConfigs = kaliumConfigs + ) } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/UserStorageProvider.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/UserStorageProvider.kt index cbcce07162b..e34b13cad5e 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/UserStorageProvider.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/UserStorageProvider.kt @@ -20,7 +20,7 @@ package com.wire.kalium.logic.di import co.touchlab.stately.collections.ConcurrentMutableMap import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.logic.util.computeIfAbsent +import com.wire.kalium.logic.util.safeComputeIfAbsent import com.wire.kalium.persistence.db.UserDatabaseBuilder import com.wire.kalium.persistence.kmmSettings.UserPrefBuilder @@ -31,7 +31,7 @@ abstract class UserStorageProvider { userId: UserId, platformUserStorageProperties: PlatformUserStorageProperties, shouldEncryptData: Boolean = true - ): UserStorage = inMemoryUserStorage.computeIfAbsent(userId) { + ): UserStorage = inMemoryUserStorage.safeComputeIfAbsent(userId) { create(userId, shouldEncryptData, platformUserStorageProperties) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScopeProvider.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScopeProvider.kt index ca7cfd8e88f..6e177095750 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScopeProvider.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScopeProvider.kt @@ -22,7 +22,7 @@ import co.touchlab.stately.collections.ConcurrentMutableMap import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.di.UserStorageProvider import com.wire.kalium.logic.feature.call.GlobalCallManager -import com.wire.kalium.logic.util.computeIfAbsent +import com.wire.kalium.logic.util.safeComputeIfAbsent interface UserSessionScopeProvider { fun get(userId: UserId): UserSessionScope? @@ -41,7 +41,7 @@ abstract class UserSessionScopeProviderCommon( } override fun getOrCreate(userId: UserId): UserSessionScope = - userScopeStorage.computeIfAbsent(userId) { + userScopeStorage.safeComputeIfAbsent(userId) { create(userId) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/auth/AuthenticationScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/auth/AuthenticationScope.kt index c6f04664a22..2b772c099fc 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/auth/AuthenticationScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/auth/AuthenticationScope.kt @@ -38,7 +38,7 @@ import com.wire.kalium.logic.feature.appVersioning.CheckIfUpdateRequiredUseCaseI import com.wire.kalium.logic.feature.auth.sso.SSOLoginScope import com.wire.kalium.logic.feature.auth.verification.RequestSecondFactorVerificationCodeUseCase import com.wire.kalium.logic.feature.register.RegisterScope -import com.wire.kalium.logic.util.computeIfAbsent +import com.wire.kalium.logic.util.safeComputeIfAbsent import com.wire.kalium.network.NetworkStateObserver import com.wire.kalium.network.networkContainer.UnauthenticatedNetworkContainer import com.wire.kalium.network.session.CertificatePinning @@ -59,7 +59,7 @@ class AuthenticationScopeProvider internal constructor( networkStateObserver: NetworkStateObserver, certConfig: () -> CertificatePinning ): AuthenticationScope = - authenticationScopeStorage.computeIfAbsent(serverConfig to proxyCredentials) { + authenticationScopeStorage.safeComputeIfAbsent(serverConfig to proxyCredentials) { AuthenticationScope( userAgent, serverConfig, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/util/CommonUtils.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/util/CommonUtils.kt index 8ad1d6dfb10..32a5b568f1e 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/util/CommonUtils.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/util/CommonUtils.kt @@ -86,7 +86,7 @@ fun Int?.isGreaterThan(other: Int?): Boolean { } // Convenience method to compute a value if absent in a "Stately" concurrent map -fun ConcurrentMutableMap.computeIfAbsent(key: K, f: () -> V): V { +fun ConcurrentMutableMap.safeComputeIfAbsent(key: K, f: () -> V): V { return this.block { if (this.containsKey(key)) return@block this[key]!! val value = f() diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/ConcurrentMutableMapTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/ConcurrentMutableMapTest.kt new file mode 100644 index 00000000000..8b26069137c --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/ConcurrentMutableMapTest.kt @@ -0,0 +1,33 @@ +/* + * Wire + * Copyright (C) 2023 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.util + +import co.touchlab.stately.collections.ConcurrentMutableMap +import kotlin.test.Test +import kotlin.test.assertEquals + +class ConcurrentMutableMapTest { + + @Test + fun givenConcurrentMap_whenSafeComputeIfAbsentIsCalledWith_thenTheSecondIsIgnored() { + val map = ConcurrentMutableMap() + map.safeComputeIfAbsent("a") { "c" } + map.safeComputeIfAbsent("a") { "d" } + assertEquals("c", map["a"]) + } +}