Fix deadlocks in gallery adder, favorites sync and backup/restore

This commit is contained in:
NerdNumber9 2019-04-14 18:20:22 -04:00
parent 24e10d6037
commit 6951ce34c7
3 changed files with 29 additions and 22 deletions

View File

@ -46,11 +46,13 @@ interface LewdSource<M : RaisedSearchMetadata, I> : CatalogueSource {
fun parseToManga(manga: SManga, input: I): Completable { fun parseToManga(manga: SManga, input: I): Completable {
val mangaId = (manga as? Manga)?.id val mangaId = (manga as? Manga)?.id
val metaObservable = if(mangaId != null) { val metaObservable = if(mangaId != null) {
db.getFlatMetadataForManga(mangaId).asRxSingle() // We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions
.map { Single.fromCallable {
if(it != null) it.raise(metaClass) db.getFlatMetadataForManga(mangaId).executeAsBlocking()
else newMetaInstance() } .map {
} if(it != null) it.raise(metaClass)
else newMetaInstance()
}
} else { } else {
Single.just(newMetaInstance()) Single.just(newMetaInstance())
} }
@ -76,10 +78,12 @@ interface LewdSource<M : RaisedSearchMetadata, I> : CatalogueSource {
*/ */
fun getOrLoadMetadata(mangaId: Long?, inputProducer: () -> Single<I>): Single<M> { fun getOrLoadMetadata(mangaId: Long?, inputProducer: () -> Single<I>): Single<M> {
val metaObservable = if(mangaId != null) { val metaObservable = if(mangaId != null) {
db.getFlatMetadataForManga(mangaId).asRxSingle() // We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions
.map { Single.fromCallable {
it?.raise(metaClass) db.getFlatMetadataForManga(mangaId).executeAsBlocking()
} }.map {
it?.raise(metaClass)
}
} else Single.just(null) } else Single.just(null)
return metaObservable.flatMap { existingMeta -> return metaObservable.flatMap { existingMeta ->

View File

@ -3,6 +3,7 @@ package exh.favorites
import android.content.Context import android.content.Context
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.os.PowerManager import android.os.PowerManager
import com.elvishew.xlog.XLog
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -21,7 +22,6 @@ import exh.util.trans
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Request import okhttp3.Request
import rx.subjects.BehaviorSubject import rx.subjects.BehaviorSubject
import timber.log.Timber
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -47,6 +47,8 @@ class FavoritesSyncHelper(val context: Context) {
private var wifiLock: WifiManager.WifiLock? = null private var wifiLock: WifiManager.WifiLock? = null
private var wakeLock: PowerManager.WakeLock? = null private var wakeLock: PowerManager.WakeLock? = null
private val logger = XLog.tag("EHFavSync").build()
val status = BehaviorSubject.create<FavoritesSyncStatus>(FavoritesSyncStatus.Idle()) val status = BehaviorSubject.create<FavoritesSyncStatus>(FavoritesSyncStatus.Idle())
@Synchronized @Synchronized
@ -73,7 +75,7 @@ class FavoritesSyncHelper(val context: Context) {
exh.fetchFavorites() exh.fetchFavorites()
} catch(e: Exception) { } catch(e: Exception) {
status.onNext(FavoritesSyncStatus.Error("Failed to fetch favorites from remote server!")) status.onNext(FavoritesSyncStatus.Error("Failed to fetch favorites from remote server!"))
Timber.e(e, "Could not fetch favorites!") logger.e( "Could not fetch favorites!", e)
return return
} }
@ -125,11 +127,11 @@ class FavoritesSyncHelper(val context: Context) {
} }
} catch(e: IgnoredException) { } catch(e: IgnoredException) {
//Do not display error as this error has already been reported //Do not display error as this error has already been reported
Timber.w(e, "Ignoring exception!") logger.w( "Ignoring exception!", e)
return return
} catch (e: Exception) { } catch (e: Exception) {
status.onNext(FavoritesSyncStatus.Error("Unknown error: ${e.message}")) status.onNext(FavoritesSyncStatus.Error("Unknown error: ${e.message}"))
Timber.e(e, "Sync error!") logger.e( "Sync error!", e)
return return
} finally { } finally {
//Release wake + wifi locks //Release wake + wifi locks
@ -228,7 +230,7 @@ class FavoritesSyncHelper(val context: Context) {
break break
} }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "Sync network error!") logger.w( "Sync network error!", e)
} }
} }
@ -381,7 +383,7 @@ sealed class FavoritesSyncStatus(val message: String) {
class Idle : FavoritesSyncStatus("Waiting for sync to start") class Idle : FavoritesSyncStatus("Waiting for sync to start")
class Initializing : FavoritesSyncStatus("Initializing sync") class Initializing : FavoritesSyncStatus("Initializing sync")
class Processing(message: String, isThrottle: Boolean = false) : FavoritesSyncStatus(if(isThrottle) class Processing(message: String, isThrottle: Boolean = false) : FavoritesSyncStatus(if(isThrottle)
(message + "\n\nSync is currently throttling (to avoid being banned from ExHentai) and may take a long time to complete.") "$message\n\nSync is currently throttling (to avoid being banned from ExHentai) and may take a long time to complete."
else else
message) message)
class CompleteWithErrors(messages: List<String>) : FavoritesSyncStatus(messages.joinToString("\n")) class CompleteWithErrors(messages: List<String>) : FavoritesSyncStatus(messages.joinToString("\n"))

View File

@ -24,14 +24,15 @@ data class FlatMetadata(
} }
fun DatabaseHelper.getFlatMetadataForManga(mangaId: Long): PreparedOperation<FlatMetadata?> { fun DatabaseHelper.getFlatMetadataForManga(mangaId: Long): PreparedOperation<FlatMetadata?> {
fun getSingle() = getSearchMetadataForManga(mangaId).asRxSingle().flatMap { meta -> // We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions
if(meta == null) Single.just(null) fun getSingle() = Single.fromCallable {
else Single.zip( val meta = getSearchMetadataForManga(mangaId).executeAsBlocking()
getSearchTagsForManga(mangaId).asRxSingle(), if(meta != null) {
getSearchTitlesForManga(mangaId).asRxSingle() val tags = getSearchTagsForManga(mangaId).executeAsBlocking()
) { tags, titles -> val titles = getSearchTitlesForManga(mangaId).executeAsBlocking()
FlatMetadata(meta, tags, titles) FlatMetadata(meta, tags, titles)
} } else null
} }
return object : PreparedOperation<FlatMetadata?> { return object : PreparedOperation<FlatMetadata?> {