Capture exceptions when saving

This commit is contained in:
Him188 2020-08-29 14:28:39 +08:00
parent ad53f315ac
commit 2c38ce920a
2 changed files with 63 additions and 13 deletions

View File

@ -13,14 +13,12 @@ package net.mamoe.mirai.console.data
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.internal.plugin.updateWhen import net.mamoe.mirai.console.internal.plugin.updateWhen
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.console.util.ConsoleInternalAPI
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.currentTimeMillis
import net.mamoe.mirai.utils.error
import net.mamoe.mirai.utils.withSwitch
/** /**
* 链接自动保存的 [PluginData]. * 链接自动保存的 [PluginData].
@ -51,17 +49,35 @@ public open class AutoSavePluginData private constructor(
this.storage_ = storage this.storage_ = storage
this.owner_ = owner this.owner_ = owner
owner_.coroutineContext[Job]?.invokeOnCompletion { doSave() } owner_.coroutineContext[Job]?.invokeOnCompletion {
kotlin.runCatching {
doSave()
}.onFailure { e ->
owner_.coroutineContext[CoroutineExceptionHandler]?.handleException(owner_.coroutineContext, e)
?.let { return@invokeOnCompletion }
MiraiConsole.mainLogger.error(
"An exception occurred when saving config ${this@AutoSavePluginData::class.qualifiedNameOrTip} " +
"but CoroutineExceptionHandler not found in PluginDataHolder.coroutineContext for ${owner::class.qualifiedNameOrTip}",
e
)
}
}
if (shouldPerformAutoSaveWheneverChanged()) { if (shouldPerformAutoSaveWheneverChanged()) {
owner_.launch(CoroutineName("AutoSavePluginData.timedAutoSave: ${this::class.qualifiedNameOrTip}")) { owner_.launch(CoroutineName("AutoSavePluginData.timedAutoSave: ${this::class.qualifiedNameOrTip}")) {
while (isActive) { while (isActive) {
try {
delay(autoSaveIntervalMillis_.last) // 定时自动保存一次, 用于 kts 序列化的对象 delay(autoSaveIntervalMillis_.last) // 定时自动保存一次, 用于 kts 序列化的对象
} catch (e: CancellationException) {
return@launch
}
withContext(owner_.coroutineContext) {
doSave() doSave()
} }
} }
} }
} }
}
@JvmField @JvmField
@Volatile @Volatile
@ -78,19 +94,29 @@ public open class AutoSavePluginData private constructor(
return true return true
} }
private val updaterBlock: suspend CoroutineScope.() -> Unit = { private val updaterBlock: suspend CoroutineScope.() -> Unit = l@{
if (::storage_.isInitialized) { if (::storage_.isInitialized) {
currentFirstStartTime_.updateWhen({ it == 0L }, { currentTimeMillis }) currentFirstStartTime_.updateWhen({ it == 0L }, { currentTimeMillis })
try {
delay(autoSaveIntervalMillis_.first.coerceAtLeast(1000)) // for safety delay(autoSaveIntervalMillis_.first.coerceAtLeast(1000)) // for safety
} catch (e: CancellationException) {
return@l
}
if (lastAutoSaveJob_ == this.coroutineContext[Job]) { if (lastAutoSaveJob_ == this.coroutineContext[Job]) {
withContext(owner_.coroutineContext) {
doSave() doSave()
}
} else { } else {
if (currentFirstStartTime_.updateWhen( if (currentFirstStartTime_.updateWhen(
{ currentTimeMillis - it >= autoSaveIntervalMillis_.last }, { currentTimeMillis - it >= autoSaveIntervalMillis_.last },
{ 0 }) { 0 })
) doSave() ) {
withContext(owner_.coroutineContext) {
doSave()
}
}
} }
} }
} }
@ -114,5 +140,25 @@ public open class AutoSavePluginData private constructor(
} }
internal val debuggingLogger1 by lazy { internal val debuggingLogger1 by lazy {
DefaultLogger("debug").withSwitch(false) DefaultLogger("debug").withSwitch(true)
}
internal inline fun <R> MiraiLogger.runCatchingLog(message: String? = null, block: () -> R): R? {
return kotlin.runCatching {
block()
}.onFailure {
if (message != null) {
error(message, it)
} else error(it)
}.getOrNull()
}
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution
internal inline fun <R> MiraiLogger.runCatchingLog(message: (Throwable) -> String, block: () -> R): R? {
return kotlin.runCatching {
block()
}.onFailure {
error(message(it), it)
}.getOrNull()
} }

View File

@ -11,6 +11,7 @@
package net.mamoe.mirai.console.data package net.mamoe.mirai.console.data
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
@ -35,6 +36,9 @@ public interface PluginDataHolder {
/** /**
* 可以持有相关 [AutoSavePluginData] 的对象. * 可以持有相关 [AutoSavePluginData] 的对象.
* *
* ### 实现 [AutoSavePluginDataHolder]
* [CoroutineScope.coroutineContext] 中应用 [CoroutineExceptionHandler]
*
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin * @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI