mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 11:05:25 +00:00
Config lock contention and loading timeout issue
This commit is contained in:
parent
966b8e0d7f
commit
db8784f0db
@ -52,8 +52,11 @@ import org.thoughtcrime.securesms.database.ConfigDatabase
|
|||||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
import org.thoughtcrime.securesms.groups.GroupManager
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -78,8 +81,8 @@ class ConfigFactory @Inject constructor(
|
|||||||
System.loadLibrary("session_util")
|
System.loadLibrary("session_util")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val userConfigs = HashMap<AccountId, UserConfigsImpl>()
|
private val userConfigs = HashMap<AccountId, Pair<ReentrantReadWriteLock, UserConfigsImpl>>()
|
||||||
private val groupConfigs = HashMap<AccountId, GroupConfigsImpl>()
|
private val groupConfigs = HashMap<AccountId, Pair<ReentrantReadWriteLock, GroupConfigsImpl>>()
|
||||||
|
|
||||||
private val _configUpdateNotifications = MutableSharedFlow<ConfigUpdateNotification>(
|
private val _configUpdateNotifications = MutableSharedFlow<ConfigUpdateNotification>(
|
||||||
extraBufferCapacity = 5, // The notifications are normally important so we can afford to buffer a few
|
extraBufferCapacity = 5, // The notifications are normally important so we can afford to buffer a few
|
||||||
@ -97,21 +100,39 @@ class ConfigFactory @Inject constructor(
|
|||||||
"No logged in user"
|
"No logged in user"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> withUserConfigs(cb: (UserConfigs) -> T): T {
|
private fun ensureUserConfigsInitialized(): Pair<ReentrantReadWriteLock, UserConfigsImpl> {
|
||||||
val userAccountId = requiresCurrentUserAccountId()
|
val userAccountId = requiresCurrentUserAccountId()
|
||||||
val configs = synchronized(userConfigs) {
|
|
||||||
|
return synchronized(userConfigs) {
|
||||||
userConfigs.getOrPut(userAccountId) {
|
userConfigs.getOrPut(userAccountId) {
|
||||||
UserConfigsImpl(
|
ReentrantReadWriteLock() to UserConfigsImpl(
|
||||||
requiresCurrentUserED25519SecKey(),
|
userEd25519SecKey = requiresCurrentUserED25519SecKey(),
|
||||||
userAccountId,
|
userAccountId = userAccountId,
|
||||||
threadDb = threadDb,
|
threadDb = threadDb,
|
||||||
configDatabase = configDatabase,
|
configDatabase = configDatabase,
|
||||||
storage = storage.get()
|
storage = storage.get()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return synchronized(configs) {
|
private fun ensureGroupConfigsInitialized(groupId: AccountId): Pair<ReentrantReadWriteLock, GroupConfigsImpl> {
|
||||||
|
val groupAdminKey = getClosedGroup(groupId)?.adminKey
|
||||||
|
return synchronized(groupConfigs) {
|
||||||
|
groupConfigs.getOrPut(groupId) {
|
||||||
|
ReentrantReadWriteLock() to GroupConfigsImpl(
|
||||||
|
userEd25519SecKey = requiresCurrentUserED25519SecKey(),
|
||||||
|
groupAccountId = groupId,
|
||||||
|
groupAdminKey = groupAdminKey,
|
||||||
|
configDatabase = configDatabase
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> withUserConfigs(cb: (UserConfigs) -> T): T {
|
||||||
|
val (lock, configs) = ensureUserConfigsInitialized()
|
||||||
|
return lock.read {
|
||||||
cb(configs)
|
cb(configs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,8 +143,9 @@ class ConfigFactory @Inject constructor(
|
|||||||
* @param cb A function that takes a [UserConfigsImpl] and returns a pair of the result of the operation and a boolean indicating if the configs were changed.
|
* @param cb A function that takes a [UserConfigsImpl] and returns a pair of the result of the operation and a boolean indicating if the configs were changed.
|
||||||
*/
|
*/
|
||||||
private fun <T> doWithMutableUserConfigs(cb: (UserConfigsImpl) -> Pair<T, Boolean>): T {
|
private fun <T> doWithMutableUserConfigs(cb: (UserConfigsImpl) -> Pair<T, Boolean>): T {
|
||||||
val (result, changed) = withUserConfigs { configs ->
|
val (lock, configs) = ensureUserConfigsInitialized()
|
||||||
cb(configs as UserConfigsImpl)
|
val (result, changed) = lock.write {
|
||||||
|
cb(configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@ -160,25 +182,9 @@ class ConfigFactory @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> withGroupConfigs(groupId: AccountId, cb: (GroupConfigs) -> T): T {
|
override fun <T> withGroupConfigs(groupId: AccountId, cb: (GroupConfigs) -> T): T {
|
||||||
val groupAdminKey = getClosedGroup(groupId)?.adminKey
|
val (lock, configs) = ensureGroupConfigsInitialized(groupId)
|
||||||
|
|
||||||
val configs = synchronized(groupConfigs) {
|
return lock.read {
|
||||||
var value = groupConfigs[groupId]
|
|
||||||
if (value == null || value.groupKeys.admin() != (groupAdminKey != null)) {
|
|
||||||
// No existing configs or existing configs have different admin settings with what we currently have
|
|
||||||
// Create a new group configs
|
|
||||||
value = GroupConfigsImpl(
|
|
||||||
requiresCurrentUserED25519SecKey(),
|
|
||||||
groupId,
|
|
||||||
groupAdminKey,
|
|
||||||
configDatabase
|
|
||||||
).also { groupConfigs[groupId] = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
return synchronized(configs) {
|
|
||||||
cb(configs)
|
cb(configs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,11 +194,14 @@ class ConfigFactory @Inject constructor(
|
|||||||
recreateConfigInstances: Boolean,
|
recreateConfigInstances: Boolean,
|
||||||
cb: (GroupConfigsImpl) -> Pair<T, Boolean>): T {
|
cb: (GroupConfigsImpl) -> Pair<T, Boolean>): T {
|
||||||
if (recreateConfigInstances) {
|
if (recreateConfigInstances) {
|
||||||
|
synchronized(groupConfigs) {
|
||||||
groupConfigs.remove(groupId)
|
groupConfigs.remove(groupId)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val (result, changed) = withGroupConfigs(groupId) { configs ->
|
val (lock, configs) = ensureGroupConfigsInitialized(groupId)
|
||||||
cb(configs as GroupConfigsImpl)
|
val (result, changed) = lock.write {
|
||||||
|
cb(configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
|
@ -80,7 +80,7 @@ internal class LoadingViewModel @Inject constructor(
|
|||||||
!configs.userProfile.getName().isNullOrEmpty()
|
!configs.userProfile.getName().isNullOrEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .timeout(TIMEOUT_TIME)
|
.timeout(TIMEOUT_TIME)
|
||||||
.first()
|
.first()
|
||||||
onSuccess()
|
onSuccess()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Loading…
Reference in New Issue
Block a user