diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9e3cb52b..2a973b84 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -4,7 +4,7 @@ androidCompileSdk = "33"
androidGradlePlugin = "7.4.2"
androidTargetSdk = "33"
atomicFu = "0.24.0"
-baseKotlin = "2.0.0"
+baseKotlin = "2.0.20"
dokkaGradlePlugin = "1.9.10"
ktlintGradle = "12.1.0"
jacocoGradlePlugin = "0.8.7"
@@ -14,10 +14,10 @@ pagingCompose = "3.3.0-alpha02"
pagingRuntime = "3.2.1"
spotlessPluginGradle = "6.4.1"
junit = "4.13.2"
-kotlinxCoroutines = "1.8.0"
-kotlinxSerialization = "1.5.1"
+kotlinxCoroutines = "1.8.1"
+kotlinxSerialization = "1.6.3"
kermit = "2.0.4"
-testCore = "1.5.0"
+testCore = "1.6.1"
kmmBridge = "0.3.2"
ktlint = "0.39.0"
kover = "0.6.0"
diff --git a/paging/kover/coverage.xml b/paging/kover/coverage.xml
index 13254ba4..a2384593 100644
--- a/paging/kover/coverage.xml
+++ b/paging/kover/coverage.xml
@@ -2,20 +2,15 @@
-
-
-
-
-
-
+
-
-
+
+
@@ -278,10 +273,9 @@
-
-
+
-
+
@@ -449,10 +443,10 @@
-
+
-
-
+
+
@@ -1066,10 +1060,10 @@
-
+
-
-
+
+
diff --git a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/impl/RealMutableStore.kt b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/impl/RealMutableStore.kt
index 84fd0294..d798d57f 100644
--- a/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/impl/RealMutableStore.kt
+++ b/store/src/commonMain/kotlin/org/mobilenativefoundation/store/store5/impl/RealMutableStore.kt
@@ -201,16 +201,11 @@ internal class RealMutableStore withThreadSafety(
key: Key,
block: suspend ThreadSafety.() -> Output,
- ): Output {
- storeLock.lock()
- try {
+ ): Output =
+ storeLock.withLock {
val threadSafety = requireNotNull(keyToThreadSafety[key])
- val output = threadSafety.block()
- return output
- } finally {
- storeLock.unlock()
+ threadSafety.block()
}
- }
private suspend fun conflictsMightExist(key: Key): Boolean {
val lastFailedSync = bookkeeper?.getLastFailedSync(key)
diff --git a/store/src/commonTest/kotlin/org/mobilenativefoundation/store/store5/StoreWithInMemoryCacheTests.kt b/store/src/commonTest/kotlin/org/mobilenativefoundation/store/store5/StoreWithInMemoryCacheTests.kt
index ba3a0328..119ccd3a 100644
--- a/store/src/commonTest/kotlin/org/mobilenativefoundation/store/store5/StoreWithInMemoryCacheTests.kt
+++ b/store/src/commonTest/kotlin/org/mobilenativefoundation/store/store5/StoreWithInMemoryCacheTests.kt
@@ -1,23 +1,21 @@
package org.mobilenativefoundation.store.store5
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
+import org.mobilenativefoundation.store.core5.ExperimentalStoreApi
import org.mobilenativefoundation.store.store5.impl.extensions.get
import kotlin.test.Test
import kotlin.test.assertEquals
-import kotlin.test.assertIs
-import kotlin.test.assertNotNull
import kotlin.time.Duration.Companion.hours
+@OptIn(ExperimentalStoreApi::class)
@FlowPreview
@ExperimentalCoroutinesApi
class StoreWithInMemoryCacheTests {
@@ -51,82 +49,86 @@ class StoreWithInMemoryCacheTests {
@Test
fun storeDeadlock() =
- testScope.runTest {
- repeat(1000) {
- val store =
+ runTest {
+ repeat(100) {
+ val store: MutableStore =
StoreBuilder
.from(
- fetcher = Fetcher.of { key: Int -> "fetcher_${key}" },
- sourceOfTruth = SourceOfTruth.Companion.of(
- reader = { key ->
- flow {
- emit("source_of_truth_${key}")
- }
- },
- writer = { key: Int, local: String ->
-
- }
- )
+ fetcher = Fetcher.of { key: Int -> "fetcher_$key" },
+ sourceOfTruth =
+ SourceOfTruth.of(
+ reader = { key: Int ->
+ flowOf("source_of_truth_$key")
+ },
+ writer = { key: Int, local: String -> },
+ ),
)
.disableCache()
.toMutableStoreBuilder(
- converter = object : Converter {
- override fun fromNetworkToLocal(network: String): String {
- return network
- }
+ converter =
+ object : Converter {
+ override fun fromNetworkToLocal(network: String): String = network
- override fun fromOutputToLocal(output: String): String {
- return output
- }
- },
+ override fun fromOutputToLocal(output: String): String = output
+ },
)
.build(
- updater = object : Updater {
- var callCount = -1
- override suspend fun post(key: Int, value: String): UpdaterResult {
- callCount += 1
- if (callCount % 2 == 0) {
- throw IllegalArgumentException(key.toString() + "value:$value")
- } else {
- return UpdaterResult.Success.Untyped("")
- }
- }
+ updater =
+ object : Updater {
+ var callCount = -1
- override val onCompletion: OnUpdaterCompletion?
- get() = null
+ override suspend fun post(
+ key: Int,
+ value: String,
+ ): UpdaterResult {
+ callCount += 1
+ return if (callCount % 2 == 0) {
+ throw IllegalArgumentException("$key value: $value")
+ } else {
+ UpdaterResult.Success.Untyped("")
+ }
+ }
- }
+ override val onCompletion: OnUpdaterCompletion? = null
+ },
)
val jobs = mutableListOf()
jobs.add(
store.stream(StoreReadRequest.cached(1, refresh = true))
.mapNotNull { it.dataOrNull() }
- .launchIn(CoroutineScope(Dispatchers.Default))
+ .launchIn(this),
)
- val job1 = store.stream(StoreReadRequest.cached(0, refresh = true))
- .mapNotNull { it.dataOrNull() }
- .launchIn(CoroutineScope(Dispatchers.Default))
+ val job1 =
+ store.stream(StoreReadRequest.cached(0, refresh = true))
+ .mapNotNull { it.dataOrNull() }
+ .launchIn(this)
jobs.add(
store.stream(StoreReadRequest.cached(2, refresh = true))
.mapNotNull { it.dataOrNull() }
- .launchIn(CoroutineScope(Dispatchers.Default)))
+ .launchIn(this),
+ )
jobs.add(
store.stream(StoreReadRequest.cached(3, refresh = true))
.mapNotNull { it.dataOrNull() }
- .launchIn(CoroutineScope(Dispatchers.Default)))
+ .launchIn(this),
+ )
job1.cancel()
assertEquals(
expected = "source_of_truth_0",
- actual = store.stream(StoreReadRequest.cached(0, refresh = true))
- .mapNotNull { it.dataOrNull() }.first()
+ actual =
+ store.stream(StoreReadRequest.cached(0, refresh = true))
+ .mapNotNull { it.dataOrNull() }
+ .first(),
)
jobs.forEach {
it.cancel()
assertEquals(
expected = "source_of_truth_0",
- actual = store.stream(StoreReadRequest.cached(0, refresh = true))
- .mapNotNull { it.dataOrNull() }.first()
+ actual =
+ store.stream(StoreReadRequest.cached(0, refresh = true))
+ .mapNotNull { it.dataOrNull() }
+ .first(),
)
}
}