Ensure PageLoader not closed until decoder jobs completed (#1923)
* Optimize to use autoCloseScope * Refine log * Log * Remove debug logging * Refine * Refine --------- Co-authored-by: Dude so hot <djohn@fbi.gov>
This commit is contained in:
1 parent
c2c86ff58e
commit
dd1bbf1226
4 files changed
+28
-33
No files matched your search
@@ -284,6 +284,7 @@ kotlin {
|
||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||
"-opt-in=androidx.compose.animation.ExperimentalSharedTransitionApi",
|
||||
"-opt-in=androidx.paging.ExperimentalPagingApi",
|
||||
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 Tarsin Norbin
|
||||
* Copyright 2023-2024 Tarsin Norbin
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.gallery
|
||||
|
||||
import arrow.fx.coroutines.autoCloseable
|
||||
import arrow.fx.coroutines.closeable
|
||||
import arrow.fx.coroutines.resourceScope
|
||||
import arrow.autoCloseScope
|
||||
import com.hippo.ehviewer.Settings.archivePasswds
|
||||
import com.hippo.ehviewer.image.ImageSource
|
||||
import com.hippo.ehviewer.image.byteBufferSource
|
||||
@@ -29,12 +27,10 @@ import com.hippo.ehviewer.jni.needPassword
|
||||
import com.hippo.ehviewer.jni.openArchive
|
||||
import com.hippo.ehviewer.jni.providePassword
|
||||
import com.hippo.ehviewer.jni.releaseByteBuffer
|
||||
import com.hippo.ehviewer.ui.screen.implicit
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.displayName
|
||||
import com.hippo.files.openFileDescriptor
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import okio.Path
|
||||
|
||||
@@ -49,19 +45,19 @@ suspend fun <T> useArchivePageLoader(
|
||||
hasAds: Boolean = false,
|
||||
passwdProvider: PasswdProvider = emptyPasswdProvider,
|
||||
block: suspend (PageLoader) -> T,
|
||||
) = coroutineScope {
|
||||
resourceScope {
|
||||
val pfd = closeable { file.openFileDescriptor("r") }
|
||||
val size = install(
|
||||
) = autoCloseScope {
|
||||
coroutineScope {
|
||||
val pfd = install(file.openFileDescriptor("r"))
|
||||
val size = autoClose(
|
||||
{ openArchive(pfd.fd, pfd.statSize, gid == 0L || file.name.endsWith(".zip")) },
|
||||
{ _, _ -> closeArchive() },
|
||||
)
|
||||
check(size > 0) { "Archive have no content!" }
|
||||
if (needPassword() && archivePasswds.filterNotNull().none { providePassword(it) }) {
|
||||
archivePasswds += passwdProvider { providePassword(it) }
|
||||
if (needPassword() && archivePasswds.filterNotNull().none(::providePassword)) {
|
||||
archivePasswds += passwdProvider(::providePassword)
|
||||
}
|
||||
val loader = autoCloseable {
|
||||
object : PageLoader(gid, startPage, size, hasAds, implicit<CoroutineScope>()) {
|
||||
val loader = install(
|
||||
object : PageLoader(gid, startPage, size, hasAds) {
|
||||
override val title by lazy { FileUtils.getNameFromFilename(file.displayName)!! }
|
||||
|
||||
override fun getImageExtension(index: Int) = getExtension(index)
|
||||
@@ -85,8 +81,8 @@ suspend fun <T> useArchivePageLoader(
|
||||
override fun prefetchPages(pages: List<Int>, bounds: IntRange) = pages.forEach(::notifySourceReady)
|
||||
|
||||
override fun onRequest(index: Int, force: Boolean, orgImg: Boolean) = notifySourceReady(index)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
loader.progressJob.join()
|
||||
block(loader)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package com.hippo.ehviewer.gallery
|
||||
|
||||
import arrow.fx.coroutines.autoCloseable
|
||||
import arrow.fx.coroutines.resourceScope
|
||||
import arrow.autoCloseScope
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.spider.SpiderQueen
|
||||
import com.hippo.ehviewer.spider.SpiderQueen.Companion.obtainSpiderQueen
|
||||
import com.hippo.ehviewer.spider.SpiderQueen.Companion.releaseSpiderQueen
|
||||
import com.hippo.ehviewer.spider.SpiderQueen.OnSpiderListener
|
||||
import com.hippo.ehviewer.ui.screen.implicit
|
||||
import com.hippo.ehviewer.util.hasAds
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import okio.Path
|
||||
|
||||
@@ -18,15 +15,15 @@ suspend fun <T> useEhPageLoader(
|
||||
info: GalleryInfo,
|
||||
startPage: Int,
|
||||
block: suspend (PageLoader) -> T,
|
||||
) = coroutineScope {
|
||||
resourceScope {
|
||||
val queen = install(
|
||||
) = autoCloseScope {
|
||||
coroutineScope {
|
||||
val queen = autoClose(
|
||||
{ obtainSpiderQueen(info, SpiderQueen.MODE_READ) },
|
||||
{ queen, _ -> releaseSpiderQueen(queen, SpiderQueen.MODE_READ) },
|
||||
)
|
||||
queen.awaitReady()
|
||||
val loader = autoCloseable {
|
||||
object : PageLoader(info.gid, startPage, queen.size, info.hasAds, implicit<CoroutineScope>()) {
|
||||
val loader = install(
|
||||
object : PageLoader(info.gid, startPage, queen.size, info.hasAds) {
|
||||
override val title by lazy { EhUtils.getSuitableTitle(info) }
|
||||
|
||||
override fun getImageExtension(index: Int) = queen.getExtension(index)
|
||||
@@ -38,8 +35,8 @@ suspend fun <T> useEhPageLoader(
|
||||
override fun prefetchPages(pages: List<Int>, bounds: IntRange) = queen.preloadPages(pages, bounds)
|
||||
|
||||
override fun onRequest(index: Int, force: Boolean, orgImg: Boolean) = queen.request(index, force, orgImg)
|
||||
}
|
||||
}.apply {
|
||||
},
|
||||
).apply {
|
||||
val listener = object : OnSpiderListener {
|
||||
override fun onPageDownload(index: Int, contentLength: Long, receivedSize: Long, bytesRead: Int) {
|
||||
if (contentLength > 0) {
|
||||
@@ -51,12 +48,12 @@ suspend fun <T> useEhPageLoader(
|
||||
|
||||
override fun onPageFailure(index: Int, error: String?, finished: Int, downloaded: Int, total: Int) = notifyPageFailed(index, error)
|
||||
}
|
||||
install(
|
||||
autoClose(
|
||||
{ queen.addOnSpiderListener(listener) },
|
||||
{ _, _ -> queen.removeOnSpiderListener(listener) },
|
||||
)
|
||||
progressJob.join()
|
||||
}
|
||||
loader.progressJob.join()
|
||||
block(loader)
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,8 @@ private val progressScope = CoroutineScope(Dispatchers.IO)
|
||||
private const val MAX_CACHE_SIZE = 512 * 1024 * 1024
|
||||
private const val MIN_CACHE_SIZE = 128 * 1024 * 1024
|
||||
|
||||
abstract class PageLoader(val gid: Long, var startPage: Int, val size: Int, val hasAds: Boolean = false, val scope: CoroutineScope) : AutoCloseable {
|
||||
context(CoroutineScope)
|
||||
abstract class PageLoader(val gid: Long, var startPage: Int, val size: Int, val hasAds: Boolean = false) : AutoCloseable {
|
||||
private val mutex = NamedMutex<Int>()
|
||||
private val semaphore = Semaphore(4)
|
||||
|
||||
@@ -61,6 +62,7 @@ abstract class PageLoader(val gid: Long, var startPage: Int, val size: Int, val
|
||||
pages[index].status !is PageStatus.Blocked
|
||||
|
||||
suspend fun atomicallyDecodeAndUpdate(index: Int) {
|
||||
if (!needDecode(index)) return
|
||||
try {
|
||||
bracketCase(
|
||||
{ openSource(index) },
|
||||
@@ -171,11 +173,10 @@ abstract class PageLoader(val gid: Long, var startPage: Int, val size: Int, val
|
||||
|
||||
fun notifySourceReady(index: Int) {
|
||||
if (needDecode(index)) {
|
||||
scope.launch {
|
||||
launch {
|
||||
mutex.withLock(index) {
|
||||
semaphore.withPermit {
|
||||
// Double check
|
||||
if (needDecode(index)) atomicallyDecodeAndUpdate(index)
|
||||
atomicallyDecodeAndUpdate(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user