Fix thread starvation caused by not yielding or using an inappropriate thread pool (#2955)

This commit is contained in:
Luca Auer
2026-02-15 16:47:31 +01:00
committed by GitHub
parent 5be5a4e819
commit 22d5c9d9f4
16 changed files with 162 additions and 83 deletions
@@ -6,6 +6,8 @@ import com.jakewharton.disklrucache.DiskLruCache
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import logcat.LogPriority
import okhttp3.Response
@@ -42,17 +44,13 @@ class ChapterCache(
*/
private val cacheDir: File = diskCache.directory
/**
* Returns real size of directory.
*/
private val realSize: Long
get() = DiskUtil.getDirectorySize(cacheDir)
/**
* Returns real size of directory in human readable format.
*/
val readableSize: String
get() = Formatter.formatFileSize(context, realSize)
suspend fun getReadableSize(): String = withContext(Dispatchers.IO) {
val size = DiskUtil.getDirectorySize(cacheDir)
Formatter.formatFileSize(context, size)
}
/**
* Get page list from cache.
@@ -104,10 +104,10 @@ class DownloadManager(
return queueState.value.find { it.chapter.id == chapterId }
}
fun startDownloadNow(chapterId: Long) {
suspend fun startDownloadNow(chapterId: Long) {
val existingDownload = getQueuedDownloadOrNull(chapterId)
// If not in queue try to start a new download
val toAdd = existingDownload ?: runBlocking { Download.fromChapterId(chapterId) } ?: return
val toAdd = existingDownload ?: Download.fromChapterId(chapterId) ?: return
queueState.value.toMutableList().apply {
existingDownload?.let { remove(it) }
add(0, toAdd)
@@ -90,7 +90,7 @@ class DownloadStore(
/**
* Returns the list of downloads to restore. It should be called in a background thread.
*/
fun restore(): List<Download> {
suspend fun restore(): List<Download> {
val objs = preferences.all
.mapNotNull { it.value as? String }
.mapNotNull { deserialize(it) }
@@ -101,10 +101,10 @@ class DownloadStore(
val cachedManga = mutableMapOf<Long, Manga?>()
for ((mangaId, chapterId) in objs) {
val manga = cachedManga.getOrPut(mangaId) {
runBlocking { getManga.await(mangaId) }
getManga.await(mangaId)
} ?: continue
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
val chapter = runBlocking { getChapter.await(chapterId) } ?: continue
val chapter = getChapter.await(chapterId) ?: continue
downloads.add(Download(source, manga, chapter))
}
}
@@ -111,9 +111,9 @@ class Downloader(
var isPaused: Boolean = false
init {
launchNow {
val chapters = async { store.restore() }
addAllToQueue(chapters.await())
scope.launch {
val chapters = store.restore()
addAllToQueue(chapters)
}
}
@@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.runBlocking
import tachiyomi.core.common.Constants
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.model.Chapter
@@ -78,11 +79,18 @@ class NotificationReceiver : BroadcastReceiver() {
ACTION_CANCEL_APP_UPDATE_DOWNLOAD -> cancelDownloadAppUpdate(context)
// Open reader activity
ACTION_OPEN_CHAPTER -> {
openChapter(
context,
intent.getLongExtra(EXTRA_MANGA_ID, -1),
intent.getLongExtra(EXTRA_CHAPTER_ID, -1),
)
val pendingResult = goAsync()
launchIO {
try {
openChapter(
context,
intent.getLongExtra(EXTRA_MANGA_ID, -1),
intent.getLongExtra(EXTRA_CHAPTER_ID, -1),
)
} finally {
pendingResult.finish()
}
}
}
// Mark updated manga chapters as read
ACTION_MARK_AS_READ -> {
@@ -147,16 +155,18 @@ class NotificationReceiver : BroadcastReceiver() {
* @param mangaId id of manga
* @param chapterId id of chapter
*/
private fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
val manga = runBlocking { getManga.await(mangaId) }
val chapter = runBlocking { getChapter.await(chapterId) }
if (manga != null && chapter != null) {
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
private suspend fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
val manga = getManga.await(mangaId)
val chapter = getChapter.await(chapterId)
withUIContext {
if (manga != null && chapter != null) {
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
context.startActivity(intent)
} else {
context.toast(MR.strings.chapter_error)
}
context.startActivity(intent)
} else {
context.toast(MR.strings.chapter_error)
}
}