diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ArchivePageLoader.kt b/app/src/main/java/com/hippo/ehviewer/gallery/ArchivePageLoader.kt index 6aad70c0c..86438e965 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/ArchivePageLoader.kt +++ b/app/src/main/java/com/hippo/ehviewer/gallery/ArchivePageLoader.kt @@ -34,10 +34,10 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withPermit +import moe.tarsin.coroutines.NamedMutex +import moe.tarsin.coroutines.withLock import java.nio.ByteBuffer class ArchivePageLoader(context: Context, private val uri: Uri, passwdFlow: Flow) : PageLoader2(), CoroutineScope { @@ -79,7 +79,7 @@ class ArchivePageLoader(context: Context, private val uri: Uri, passwdFlow: Flow } private val mJobMap = hashMapOf() - private val mWorkerMutex by lazy { (0 until size).map { Mutex() } } + private val mWorkerMutex = NamedMutex() private val mSemaphore = Semaphore(4) override fun onRequest(index: Int) { @@ -87,7 +87,7 @@ class ArchivePageLoader(context: Context, private val uri: Uri, passwdFlow: Flow val current = mJobMap[index] if (current?.isActive != true) { mJobMap[index] = launch { - mWorkerMutex[index].withLock { + mWorkerMutex.withLock(index) { mSemaphore.withPermit { doRealWork(index) } diff --git a/app/src/main/java/moe/tarsin/coroutines/NamedMutex.kt b/app/src/main/java/moe/tarsin/coroutines/NamedMutex.kt new file mode 100644 index 000000000..f1876fcea --- /dev/null +++ b/app/src/main/java/moe/tarsin/coroutines/NamedMutex.kt @@ -0,0 +1,29 @@ +package moe.tarsin.coroutines + +import arrow.core.memoize +import kotlinx.coroutines.sync.Mutex +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +class NamedMutex { + val innerMutexGetter = { _: T -> Mutex() }.memoize() +} + +suspend inline fun NamedMutex.withLock(key: K, owner: Any? = null, action: () -> T): T { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + + val mutex = synchronized(innerMutexGetter) { + innerMutexGetter(key) + } + + mutex.run { + lock(owner) + try { + return action() + } finally { + unlock(owner) + } + } +}