Skip to content

Commit

Permalink
Multiplatformize concurrent API usage using Stately
Browse files Browse the repository at this point in the history
  • Loading branch information
veyndan committed Jun 29, 2023
1 parent 106e67c commit 62541e8
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 33 deletions.
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ paparazziNative = "2022.1.1-canary-f5f9f71"
skiko = "0.7.7"
spdxGradlePlugin = "0.1.0"
sqldelight = "1.3.0"
stately = "2.0.0-rc3"
retrofit = "2.7.2"
wire = "4.7.0"

Expand Down Expand Up @@ -262,6 +263,8 @@ spdxGradlePluginz = { module = "org.spdx:spdx-gradle-plugin", version.ref = "spd
sqldelightAndroid = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelightCoroutinesExt = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
sqliteJdbc = { module = "org.xerial:sqlite-jdbc", version = "3.36.0" }
statelyConcurrency = { module = "co.touchlab:stately-concurrency", version.ref = "stately" }
statelyConcurrentCollections = { module = "co.touchlab:stately-concurrent-collections", version.ref = "stately" }
testCore = { module = "androidx.test:core", version.ref = "androidxTestCore" }
testCoreKtx = { module = "androidx.test:core-ktx", version.ref = "androidxTestCore" }
testExtJunit = { module = "androidx.test.ext:junit", version.ref = "androidxTestExtJunit" }
Expand Down
36 changes: 36 additions & 0 deletions gradle/verification-keyring.keys
Original file line number Diff line number Diff line change
Expand Up @@ -12159,6 +12159,42 @@ VDzc34e+Nr/b2pN05MDHA0dXmb/irwPBl0mTOgAgC805qkR14xhd1GeL6MEA34k8
=CmMl
-----END PGP PUBLIC KEY BLOCK-----

pub DE453E55DC86FC9B
uid Touchlab <build@touchlab.co>

sub A947A3FCB1697B4F
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.68

mQENBF7H/6gBCACbEuIbxWAfHEYViPqdpwxDYauxsYwk6FgA9sSO1nS95KRwx+Cs
X6F8nRGnfLtbo6Ffcp6r58fNi9RvY7ueRGiL0kQd6c5GYx6dH1b91Q1qrdVOeEdj
vjHNVVXAlk1TN2oxFB81cz737cv2CTX1ibEO+qn8oxwOssgNO8ic6szJGorFur/K
pCin+E1orZiL52+aSNtOsmzLW7qmL2VuDmoQ5guPfX7l6fioCwnUB9VA2LhD2Bm3
oV4IhhH246CZ1iXWRG+vzCFDQjjPG5oPJfPvXtTmSuD7/65vNlrRk9sAh2p0BG3I
i0k8304elsm+HnVIUDyroBjud464qc+iY2bLABEBAAG0HFRvdWNobGFiIDxidWls
ZEB0b3VjaGxhYi5jbz6JAVQEEwEIAD4WIQSy+We2fa3B8HFy29reRT5V3Ib8mwUC
Xsf/qAIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDeRT5V3Ib8
m8QCB/97mcLfMXI5MuP+dLVx0ZltfAKb/2BRfgfGHn+xt+o7jeRG5B8qkKrl8+fZ
3kS/aoW7VZu3XpHPLCiEkL5MLB45J8+AuSYKhQBrugJSmaXifgeAR1To5qoynLMV
ZY/UgnkRnL1fBZ/gjMfnJzZxn2BCtcKXwmTT5rGraaapc4Cska+ZjRvT1NIGdMmH
yHDxqsFzVVhbWXWO1xJxajP//umDC3j+gyQUI3nTWql76u/BxMaU6Oba5y4zzRQd
1xV+yCm4mcfXcvXi5rf6xYd05JAlbfbZ0xCNFGgBZhyMoTbc2SANZIvG1/v0+p8k
eZUS+HVdfWc0/7eUfru3vzkUvIphuQENBF7H/6gBCADbGrkDp9Jk+FPaV//G6Sy5
oWoK1+3Hhw0aa4sKCStrmi+g4yPQ7l6M4hLj8cSE4u6UlRwB8s/FGB6CNNqoCJPh
qdTHrYoKDnZKUOLL+8eYCnVcBoFdrJOmbx6gyaBfRoKj+EzPIgYpwnhA+evdIIXr
Fcs036YLpAZEMKrhTAPTiF3MaOhjT9JcT1LSsyABi9e/r9zBQgzr718YgvMmce72
nKt72vp1tijOHu0q3axi9I5LYt7OzBsSOmCgUndIb+JPIkd/axE77f/tznexTKEU
Aed/xtYqAOg+fffGu9gRpkZFbFNNTH3iAvLPPg4SQAF3dQ5fSwT0NEbaXq6FT+aT
ABEBAAGJATwEGAEIACYWIQSy+We2fa3B8HFy29reRT5V3Ib8mwUCXsf/qAIbDAUJ
A8JnAAAKCRDeRT5V3Ib8m2JDB/4hb/taMn+1776Dd4DRzJVwiXY6zpwUmhgMlRAm
H5qivj1vYK2CvACCf44VH03hKouUIj9ZAAuHHJjqKqBHMW+AMIn3CL+kl/2jsj8+
+CMziEtBDskrNFKYOHkQi0o+aBOv2MvOt881890JpcCIHaCmLInt29k9r7PKgHSi
FBF53/CQB2yCzwiiutA1qE+9HFUyNCVvKsIACzOpLCwDU19+8LVxbrND/ns+Tah7
3WHQrxtKeTt6aYYOuhjqIxjPPTJngBzSjNDUmOxo9F95mbffQ2h1FugmKI1xBZku
ClYd7CwAcljpFxHI6Rol9sRlTbDJvAX4aQvrFqhlD+i1X12O
=FrYN
-----END PGP PUBLIC KEY BLOCK-----

pub 0729A0AFF8999A87
sub 6005789E24E5AD1E
sub 6A0975F8B1127B83
Expand Down
2 changes: 2 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@
<trusting group="io.grpc"/>
</trusted-key>
<trusted-key id="b252e5789636134a311e4463971b04f56669b805" group="com.google.jsilver"/>
<trusted-key id="b2f967b67dadc1f07172dbdade453e55dc86fc9b" group="co.touchlab"/>
<trusted-key id="b41089a2da79b0fa5810252872385ff0af338d52" group="org.threeten"/>
<trusted-key id="b46dc71e03feeb7f89d1f2491f7a8f87b9d8f501" group="org.jetbrains.trove4j"/>
<trusted-key id="b47034c19c9b1f3dc3702f8d476634a4694e716a" group="com.googlecode.java-diff-utils"/>
Expand Down Expand Up @@ -985,6 +986,7 @@
<component group="org.sonatype.oss" name="oss-parent" version="9">
<artifact name="oss-parent-9.pom">
<pgp value="44fbdbbc1a00fe414f1c1873586654072ead6677"/>
<sha256 value="fb40265f982548212ff82e362e59732b2187ec6f0d80182885c14ef1f982827a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.tensorflow" name="tensorflow-lite-metadata" version="0.1.0-rc2">
Expand Down
2 changes: 2 additions & 0 deletions paging/paging-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ androidXMultiplatform {
dependencies {
api(libs.kotlinStdlib)
api(libs.kotlinCoroutinesCore)
implementation(libs.statelyConcurrency)
implementation(libs.statelyConcurrentCollections)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.annotation.RestrictTo
import androidx.paging.CombineSource.INITIAL
import androidx.paging.CombineSource.OTHER
import androidx.paging.CombineSource.RECEIVER
import java.util.concurrent.atomic.AtomicInteger
import co.touchlab.stately.concurrency.AtomicInt
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.SendChannel
Expand Down Expand Up @@ -137,7 +137,7 @@ internal suspend inline fun <T1, T2, R> Flow<T1>.combineWithoutBatching(
crossinline transform: suspend (T1, T2, updateFrom: CombineSource) -> R,
): Flow<R> {
return simpleChannelFlow {
val incompleteFlows = AtomicInteger(2)
val incompleteFlows = AtomicInt(2)
val unbatchedFlowCombiner = UnbatchedFlowCombiner<T1, T2> { t1, t2, updateFrom ->
send(transform(t1, t2, updateFrom))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ package androidx.paging
import androidx.annotation.RestrictTo
import androidx.paging.LoadType.APPEND
import androidx.paging.LoadType.PREPEND
import java.util.concurrent.locks.ReentrantLock
import co.touchlab.stately.concurrency.Lock
import kotlin.concurrent.withLock
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -106,7 +106,7 @@ internal class HintHandler {
get() = prepend.flow
val appendFlow
get() = append.flow
private val lock = ReentrantLock()
private val lock = Lock()

/**
* Modifies the state inside a lock where it gets access to the mutable values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package androidx.paging

import androidx.annotation.VisibleForTesting
import java.util.concurrent.locks.ReentrantLock
import co.touchlab.stately.concurrency.Lock
import kotlin.concurrent.withLock

/**
Expand All @@ -30,7 +30,7 @@ internal class InvalidateCallbackTracker<T>(
*/
private val invalidGetter: (() -> Boolean)? = null,
) {
private val lock = ReentrantLock()
private val lock = Lock()
private val callbacks = mutableListOf<T>()
internal var invalid = false
private set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
package androidx.paging

import androidx.annotation.VisibleForTesting
import java.util.concurrent.CopyOnWriteArrayList
import co.touchlab.stately.collections.ConcurrentMutableList

/**
* Wrapper class for a [PagingSource] factory intended for usage in [Pager] construction.
*
* Calling [invalidate] on this [InvalidatingPagingSourceFactory] will forward invalidate signals
* to all active [PagingSource]s that were produced by calling [invoke].
*
* This class is backed by a [CopyOnWriteArrayList], which is thread-safe for concurrent calls to
* This class is backed by a [ConcurrentMutableList], which is thread-safe for concurrent calls to
* any mutative operations including both [invoke] and [invalidate].
*
* @param pagingSourceFactory The [PagingSource] factory that returns a PagingSource when called
Expand All @@ -35,7 +35,7 @@ public class InvalidatingPagingSourceFactory<Key : Any, Value : Any>(
) : PagingSourceFactory<Key, Value> {

@VisibleForTesting
internal val pagingSources = CopyOnWriteArrayList<PagingSource<Key, Value>>()
internal val pagingSources = ConcurrentMutableList<PagingSource<Key, Value>>()

/**
* @return [PagingSource] which will be invalidated when this factory's [invalidate] method
Expand All @@ -50,11 +50,9 @@ public class InvalidatingPagingSourceFactory<Key : Any, Value : Any>(
* [InvalidatingPagingSourceFactory]
*/
public fun invalidate() {
for (pagingSource in pagingSources) {
if (!pagingSource.invalid) {
pagingSource.invalidate()
}
}
pagingSources
.filterNot { it.invalid }
.forEach { it.invalidate() }

pagingSources.removeAll { it.invalid }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package androidx.paging
import androidx.paging.LoadState.Loading
import androidx.paging.LoadState.NotLoading
import androidx.paging.PagingSource.LoadParams
import java.util.concurrent.atomic.AtomicBoolean
import co.touchlab.stately.concurrency.AtomicBoolean
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -45,7 +45,7 @@ internal class LegacyPageFetcher<K : Any, V : Any>(
}

val isDetached
get() = detached.get()
get() = detached.value

private fun scheduleLoad(type: LoadType, params: LoadParams<K>) {
// Listen on the BG thread if the paged source is invalid, since it can be expensive.
Expand Down Expand Up @@ -154,7 +154,9 @@ internal class LegacyPageFetcher<K : Any, V : Any>(
}
}

fun detach() = detached.set(true)
fun detach() {
detached.value = true
}

internal interface PageConsumer<V : Any> {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package androidx.paging
import androidx.paging.LoadState.Error
import androidx.paging.LoadState.Loading
import androidx.paging.LoadState.NotLoading
import java.util.concurrent.CopyOnWriteArrayList
import co.touchlab.stately.collections.ConcurrentMutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand All @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.update
*/
internal class MutableCombinedLoadStateCollection {

private val listeners = CopyOnWriteArrayList<(CombinedLoadStates) -> Unit>()
private val listeners = ConcurrentMutableList<(CombinedLoadStates) -> Unit>()
private val _stateFlow = MutableStateFlow<CombinedLoadStates?>(null)
public val stateFlow = _stateFlow.asStateFlow()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import androidx.paging.PagingSource.LoadParams
import androidx.paging.PagingSource.LoadResult
import androidx.paging.PagingSource.LoadResult.Page
import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED
import java.util.concurrent.atomic.AtomicBoolean
import co.touchlab.stately.concurrency.AtomicBoolean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import androidx.paging.PageEvent.StaticList
import androidx.paging.PagePresenter.ProcessPageEventCallback
import androidx.paging.internal.BUGANIZER_URL
import androidx.paging.internal.appendMediatorStatesIfNotNull
import java.util.concurrent.CopyOnWriteArrayList
import co.touchlab.stately.collections.ConcurrentMutableList
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
Expand All @@ -52,7 +52,7 @@ public abstract class PagingDataDiffer<T : Any>(
private val combinedLoadStatesCollection = MutableCombinedLoadStateCollection().apply {
cachedPagingData?.cachedEvent()?.let { set(it.sourceLoadStates, it.mediatorLoadStates) }
}
private val onPagesUpdatedListeners = CopyOnWriteArrayList<() -> Unit>()
private val onPagesUpdatedListeners = ConcurrentMutableList<() -> Unit>()

private val collectFromRunner = SingleRunner()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import androidx.paging.AccessorState.BlockState.COMPLETED
import androidx.paging.AccessorState.BlockState.REQUIRES_REFRESH
import androidx.paging.AccessorState.BlockState.UNBLOCKED
import androidx.paging.RemoteMediator.MediatorResult
import java.util.concurrent.locks.ReentrantLock
import co.touchlab.stately.concurrency.Lock
import kotlin.concurrent.withLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -67,7 +67,7 @@ internal fun <Key : Any, Value : Any> RemoteMediatorAccessor(
* Simple wrapper around the local state of accessor to ensure we don't concurrently change it.
*/
private class AccessorStateHolder<Key : Any, Value : Any> {
private val lock = ReentrantLock()
private val lock = Lock()

private val _loadStates = MutableStateFlow(LoadStates.IDLE)
val loadStates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import androidx.kruth.assertThat
import androidx.paging.ActiveFlowTracker.FlowType
import androidx.paging.ActiveFlowTracker.FlowType.PAGED_DATA_FLOW
import androidx.paging.ActiveFlowTracker.FlowType.PAGE_EVENT_FLOW
import java.util.concurrent.atomic.AtomicInteger
import co.touchlab.stately.concurrency.AtomicInt
import kotlin.test.Test
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -802,8 +802,8 @@ class CachingTest {

private class ActiveFlowTrackerImpl : ActiveFlowTracker {
private val counters = mapOf(
PAGED_DATA_FLOW to AtomicInteger(0),
PAGE_EVENT_FLOW to AtomicInteger(0)
PAGED_DATA_FLOW to AtomicInt(0),
PAGE_EVENT_FLOW to AtomicInt(0)
)

override fun onNewCachedEventFlow(cachedPageEventFlow: CachedPageEventFlow<*>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
package androidx.paging

import androidx.kruth.assertWithMessage
import co.touchlab.stately.concurrency.AtomicBoolean
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread
import kotlin.random.Random
import kotlin.reflect.KClass
Expand All @@ -45,7 +45,7 @@ internal class GarbageCollectionTestHelper {
val arraySize = Random.nextInt(1000)
leak.add(ByteArray(arraySize))
System.gc()
} while (continueTriggeringGc.get())
} while (continueTriggeringGc.value)
}
var collectedItemCount = 0
val expectedItemCount = size - expected.sumOf { it.second }
Expand All @@ -54,7 +54,7 @@ internal class GarbageCollectionTestHelper {
) {
collectedItemCount++
}
continueTriggeringGc.set(false)
continueTriggeringGc.value = false
val leakedObjects = countLiveObjects()
val leakedObjectToStrings = references.mapNotNull {
it.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import androidx.paging.RemoteMediator.InitializeAction.LAUNCH_INITIAL_REFRESH
import androidx.paging.RemoteMediator.InitializeAction.SKIP_INITIAL_REFRESH
import androidx.paging.RemoteMediatorMock.LoadEvent
import androidx.paging.TestPagingSource.Companion.LOAD_ERROR
import java.util.concurrent.atomic.AtomicBoolean
import co.touchlab.stately.concurrency.AtomicBoolean
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.fail
Expand Down Expand Up @@ -500,7 +500,7 @@ class RemoteMediatorAccessorTest {
return try {
super.load(loadType, state)
} finally {
loading.set(false)
loading.value = false
}
}
}
Expand Down Expand Up @@ -583,7 +583,7 @@ class RemoteMediatorAccessorTest {
return try {
super.load(loadType, state)
} finally {
loading.set(false)
loading.value = false
}
}
}
Expand Down

0 comments on commit 62541e8

Please sign in to comment.