From b031adabeb6e5bfb657d4fabd514023479a238f7 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 15 Apr 2023 17:59:29 -0600 Subject: [PATCH] music: correctly bubble exceptions Correctly bubble failures in the music loading process. Do it the easy way and simply map to a result, then backl to an exception. I need to actually just make it fully bubble event --- .../org/oxycblt/auxio/music/system/Indexer.kt | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt index 321724d2c..c032299ef 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt @@ -23,17 +23,10 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import androidx.core.content.ContextCompat +import kotlinx.coroutines.* import java.util.LinkedList import javax.inject.Inject -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.async import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.coroutines.yield import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.music.* import org.oxycblt.auxio.music.cache.CacheRepository @@ -44,6 +37,8 @@ import org.oxycblt.auxio.music.storage.MediaStoreExtractor import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logE import org.oxycblt.auxio.util.logW +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * Core music loading state class. @@ -351,14 +346,15 @@ constructor( // Do the initial query of the cache and media databases in parallel. logD("Starting queries") - val mediaStoreQueryJob = scope.async { mediaStoreExtractor.query() } + val mediaStoreQueryJob = scope.tryAsync { mediaStoreExtractor.query() } val cache = if (withCache) { cacheRepository.readCache() } else { null } - val query = mediaStoreQueryJob.await() + // TODO: Stupid, actually bubble results properly + val query = mediaStoreQueryJob.await().getOrThrow() // Now start processing the queried song information in parallel. Songs that can't be // received from the cache are consisted incomplete and pushed to a separate channel @@ -367,10 +363,10 @@ constructor( val completeSongs = Channel(Channel.UNLIMITED) val incompleteSongs = Channel(Channel.UNLIMITED) val mediaStoreJob = - scope.async { + scope.tryAsync { mediaStoreExtractor.consume(query, cache, incompleteSongs, completeSongs) } - val metadataJob = scope.async { tagExtractor.consume(incompleteSongs, completeSongs) } + val metadataJob = scope.tryAsync { tagExtractor.consume(incompleteSongs, completeSongs) } // Await completed raw songs as they are processed. val rawSongs = LinkedList() @@ -379,8 +375,8 @@ constructor( emitIndexing(Indexer.Indexing.Songs(rawSongs.size, query.projectedTotal)) } // These should be no-ops - mediaStoreJob.await() - metadataJob.await() + mediaStoreJob.await().getOrThrow() + metadataJob.await().getOrThrow() if (rawSongs.isEmpty()) { logE("Music library was empty") @@ -391,13 +387,21 @@ constructor( // parallel. logD("Discovered ${rawSongs.size} songs, starting finalization") emitIndexing(Indexer.Indexing.Indeterminate) - val libraryJob = scope.async(Dispatchers.Main) { Library.from(rawSongs, musicSettings) } + val libraryJob = scope.tryAsync(Dispatchers.Main) { Library.from(rawSongs, musicSettings) } if (cache == null || cache.invalidated) { cacheRepository.writeCache(rawSongs) } - return libraryJob.await() + return libraryJob.await().getOrThrow() } + private inline fun CoroutineScope.tryAsync(context: CoroutineContext = EmptyCoroutineContext, crossinline block: suspend () -> R) = async(context) { + try { + Result.success(block()) + } catch (e: Exception) { + Result.failure(e) + } + } + /** * Emit a new [Indexer.State.Indexing] state. This can be used to signal the current state of * the music loading process to external code. Assumes that the callee has already checked if