From a747f2590658ffeda748884d07d866baa4947f4a Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 5 Aug 2025 11:47:03 -0400 Subject: [PATCH] Adds exception handlers for managed coroutine scopes --- .../playback/playerPool/ExoPlayerPool.kt | 11 ++++++++++- .../PushNotificationReceiverService.kt | 9 ++++++++- .../ammolite/relays/BundledUpdate.kt | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/ExoPlayerPool.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/ExoPlayerPool.kt index 17ffeb8d6..85121a580 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/ExoPlayerPool.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/playerPool/ExoPlayerPool.kt @@ -21,9 +21,11 @@ package com.vitorpamplona.amethyst.service.playback.playerPool import android.content.Context +import android.util.Log import androidx.annotation.OptIn import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -39,7 +41,14 @@ class ExoPlayerPool( private val playerPool = ConcurrentLinkedQueue() private val poolSize = SimultaneousPlaybackCalculator.max() private val poolStartingSize = 3 - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + + // Exists to avoid exceptions stopping the coroutine + val exceptionHandler = + CoroutineExceptionHandler { _, throwable -> + Log.e("BundledInsert", "Caught exception: ${throwable.message}", throwable) + } + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main + exceptionHandler) private val mutex = Mutex() diff --git a/amethyst/src/play/java/com/vitorpamplona/amethyst/service/notifications/PushNotificationReceiverService.kt b/amethyst/src/play/java/com/vitorpamplona/amethyst/service/notifications/PushNotificationReceiverService.kt index de41b7bc5..57e045df1 100644 --- a/amethyst/src/play/java/com/vitorpamplona/amethyst/service/notifications/PushNotificationReceiverService.kt +++ b/amethyst/src/play/java/com/vitorpamplona/amethyst/service/notifications/PushNotificationReceiverService.kt @@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.getOrC import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.getOrCreateZapChannel import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -39,7 +40,13 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.launch class PushNotificationReceiverService : FirebaseMessagingService() { - private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + // Exists to avoid exceptions stopping the coroutine + val exceptionHandler = + CoroutineExceptionHandler { _, throwable -> + Log.e("AmethystCoroutine", "Caught exception: ${throwable.message}", throwable) + } + + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler) private val eventCache = LruCache(100) // this is called when a message is received diff --git a/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/BundledUpdate.kt b/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/BundledUpdate.kt index c02ffa88e..2866251b2 100644 --- a/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/BundledUpdate.kt +++ b/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/BundledUpdate.kt @@ -20,9 +20,11 @@ */ package com.vitorpamplona.ammolite.relays +import android.util.Log import androidx.compose.runtime.Stable import com.vitorpamplona.ammolite.service.checkNotInMainThread import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.NonCancellable @@ -40,7 +42,13 @@ class BundledUpdate( val delay: Long, val dispatcher: CoroutineDispatcher = Dispatchers.Default, ) { - val scope = CoroutineScope(dispatcher + SupervisorJob()) + // Exists to avoid exceptions stopping the coroutine + val exceptionHandler = + CoroutineExceptionHandler { _, throwable -> + Log.e("BundledUpdate", "Caught exception: ${throwable.message}", throwable) + } + + val scope = CoroutineScope(dispatcher + SupervisorJob() + exceptionHandler) private var onlyOneInBlock = AtomicBoolean() private var invalidatesAgain = false @@ -83,7 +91,13 @@ class BundledInsert( val delay: Long, val dispatcher: CoroutineDispatcher = Dispatchers.Default, ) { - val scope = CoroutineScope(dispatcher + SupervisorJob()) + // Exists to avoid exceptions stopping the coroutine + val exceptionHandler = + CoroutineExceptionHandler { _, throwable -> + Log.e("BundledInsert", "Caught exception: ${throwable.message}", throwable) + } + + val scope = CoroutineScope(dispatcher + SupervisorJob() + exceptionHandler) private var onlyOneInBlock = AtomicBoolean() private var queue = LinkedBlockingQueue()