mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
chore: review-cleanup fleet (audit + fix + hardening) (#5158)
This commit is contained in:
parent
872c566ef1
commit
17e69c6d4c
68 changed files with 784 additions and 459 deletions
|
|
@ -19,19 +19,31 @@ package org.meshtastic.core.prefs
|
|||
import kotlinx.atomicfu.AtomicRef
|
||||
import kotlinx.collections.immutable.PersistentMap
|
||||
|
||||
internal inline fun <K, V> cachedFlow(cache: AtomicRef<PersistentMap<K, V>>, key: K, build: () -> V): V {
|
||||
var resolved = cache.value[key]
|
||||
if (resolved == null) {
|
||||
val newValue = build()
|
||||
while (resolved == null) {
|
||||
val current = cache.value
|
||||
val currentValue = current[key]
|
||||
if (currentValue != null) {
|
||||
resolved = currentValue
|
||||
} else if (cache.compareAndSet(current, current.put(key, newValue))) {
|
||||
resolved = newValue
|
||||
}
|
||||
/**
|
||||
* Look up [key] in [cache]; if absent, construct a value via [build] and insert it atomically.
|
||||
*
|
||||
* [build] is wrapped in a [Lazy] before being published to [cache], so concurrent first-access of the same key never
|
||||
* invokes [build] more than once — only the winner of the CAS has its [Lazy] evaluated, and all readers share that same
|
||||
* result. This matters when [build] eagerly launches a coroutine (e.g. `Flow.stateIn(scope, Eagerly, …)`): the naive
|
||||
* approach would leak the losing coroutine into a never-cancelled scope.
|
||||
*/
|
||||
@Suppress("ReturnCount")
|
||||
internal inline fun <K, V> cachedFlow(
|
||||
cache: AtomicRef<PersistentMap<K, Lazy<V>>>,
|
||||
key: K,
|
||||
crossinline build: () -> V,
|
||||
): V {
|
||||
cache.value[key]?.let {
|
||||
return it.value
|
||||
}
|
||||
val newLazy = lazy(LazyThreadSafetyMode.SYNCHRONIZED) { build() }
|
||||
while (true) {
|
||||
val current = cache.value
|
||||
current[key]?.let {
|
||||
return it.value
|
||||
}
|
||||
if (cache.compareAndSet(current, current.put(key, newLazy))) {
|
||||
return newLazy.value
|
||||
}
|
||||
}
|
||||
return checkNotNull(resolved)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class MapConsentPrefsImpl(
|
|||
) : MapConsentPrefs {
|
||||
private val scope = CoroutineScope(SupervisorJob() + dispatchers.default)
|
||||
|
||||
private val consentFlows = atomic(persistentMapOf<Int?, StateFlow<Boolean>>())
|
||||
private val consentFlows = atomic(persistentMapOf<Int?, Lazy<StateFlow<Boolean>>>())
|
||||
|
||||
override fun shouldReportLocation(nodeNum: Int?): StateFlow<Boolean> = cachedFlow(consentFlows, nodeNum) {
|
||||
val key = booleanPreferencesKey(nodeNum.toString())
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package org.meshtastic.core.prefs.mesh
|
|||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
|
|
@ -45,8 +44,7 @@ class MeshPrefsImpl(
|
|||
) : MeshPrefs {
|
||||
private val scope = CoroutineScope(SupervisorJob() + dispatchers.default)
|
||||
|
||||
private val locationFlows = atomic(persistentMapOf<Int?, StateFlow<Boolean>>())
|
||||
private val storeForwardFlows = atomic(persistentMapOf<String?, StateFlow<Int>>())
|
||||
private val storeForwardFlows = atomic(persistentMapOf<String?, Lazy<StateFlow<Int>>>())
|
||||
|
||||
override val deviceAddress: StateFlow<String?> =
|
||||
dataStore.data
|
||||
|
|
@ -65,15 +63,6 @@ class MeshPrefsImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override fun shouldProvideNodeLocation(nodeNum: Int?): StateFlow<Boolean> = cachedFlow(locationFlows, nodeNum) {
|
||||
val key = booleanPreferencesKey(provideLocationKey(nodeNum))
|
||||
dataStore.data.map { it[key] ?: false }.stateIn(scope, SharingStarted.Eagerly, false)
|
||||
}
|
||||
|
||||
override fun setShouldProvideNodeLocation(nodeNum: Int?, provide: Boolean) {
|
||||
scope.launch { dataStore.edit { prefs -> prefs[booleanPreferencesKey(provideLocationKey(nodeNum))] = provide } }
|
||||
}
|
||||
|
||||
override fun getStoreForwardLastRequest(address: String?): StateFlow<Int> = cachedFlow(storeForwardFlows, address) {
|
||||
val key = intPreferencesKey(storeForwardKey(address))
|
||||
dataStore.data.map { it[key] ?: 0 }.stateIn(scope, SharingStarted.Eagerly, 0)
|
||||
|
|
@ -92,8 +81,6 @@ class MeshPrefsImpl(
|
|||
}
|
||||
}
|
||||
|
||||
private fun provideLocationKey(nodeNum: Int?) = "provide-location-$nodeNum"
|
||||
|
||||
private fun storeForwardKey(address: String?): String = "store-forward-last-request-${normalizeAddress(address)}"
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class UiPrefsImpl(
|
|||
private val scope = CoroutineScope(SupervisorJob() + dispatchers.default)
|
||||
|
||||
// Maps nodeNum to a flow for the for the "provide-location-nodeNum" pref
|
||||
private val provideNodeLocationFlows = atomic(persistentMapOf<Int, StateFlow<Boolean>>())
|
||||
private val provideNodeLocationFlows = atomic(persistentMapOf<Int, Lazy<StateFlow<Boolean>>>())
|
||||
|
||||
override val appIntroCompleted: StateFlow<Boolean> =
|
||||
dataStore.data.map { it[KEY_APP_INTRO_COMPLETED] ?: false }.stateIn(scope, SharingStarted.Eagerly, false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue