mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-11 06:24:49 +08:00
Fix unmanaged coroutine job, add names to all CoroutineScopes
This commit is contained in:
parent
c479856380
commit
bbf2ead3f0
@ -108,7 +108,7 @@ public interface MiraiConsole : CoroutineScope {
|
||||
Bot(id, password) {
|
||||
fileBasedDeviceInfo()
|
||||
redirectNetworkLogToDirectory()
|
||||
parentCoroutineContext = MiraiConsole.childScopeContext()
|
||||
parentCoroutineContext = MiraiConsole.childScopeContext("Bot $id")
|
||||
|
||||
this.loginSolver = MiraiConsoleImplementationBridge.createLoginSolver(id, this)
|
||||
configuration()
|
||||
|
@ -27,6 +27,7 @@ import net.mamoe.mirai.console.util.BotManager.INSTANCE.removeManager
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.event.events.EventCancelledException
|
||||
import net.mamoe.mirai.getFriendOrNull
|
||||
import net.mamoe.mirai.message.nextMessageOrNull
|
||||
import net.mamoe.mirai.utils.secondsToMillis
|
||||
@ -113,19 +114,27 @@ public object BuiltInCommands {
|
||||
|
||||
@Handler
|
||||
public suspend fun CommandSender.handle() {
|
||||
closingLock.withLock {
|
||||
sendMessage("Stopping mirai-console")
|
||||
kotlin.runCatching {
|
||||
MiraiConsole.job.cancelAndJoin()
|
||||
}.fold(
|
||||
onSuccess = { sendMessage("mirai-console stopped successfully.") },
|
||||
onFailure = {
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
MiraiConsole.mainLogger.error(it)
|
||||
sendMessage(it.localizedMessage ?: it.message ?: it.toString())
|
||||
}
|
||||
)
|
||||
}
|
||||
kotlin.runCatching {
|
||||
closingLock.withLock {
|
||||
sendMessage("Stopping mirai-console")
|
||||
kotlin.runCatching {
|
||||
MiraiConsole.job.cancelAndJoin()
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
ignoreException<EventCancelledException> { sendMessage("mirai-console stopped successfully.") }
|
||||
},
|
||||
onFailure = {
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
MiraiConsole.mainLogger.error(it)
|
||||
ignoreException<EventCancelledException> {
|
||||
sendMessage(
|
||||
it.localizedMessage ?: it.message ?: it.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}.exceptionOrNull()?.let(MiraiConsole.mainLogger::error)
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
@ -158,6 +167,24 @@ public object BuiltInCommands {
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified E : Throwable, R> ignoreException(block: () -> R): R? {
|
||||
try {
|
||||
return block()
|
||||
} catch (e: Throwable) {
|
||||
if (e is E) return null
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified E : Throwable> ignoreException(block: () -> Unit): Unit? {
|
||||
try {
|
||||
return block()
|
||||
} catch (e: Throwable) {
|
||||
if (e is E) return null
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ContactOrBot.render(): String {
|
||||
return when (this) {
|
||||
is Bot -> "Bot $nick($id)"
|
||||
|
@ -28,6 +28,7 @@ public interface PluginDataHolder {
|
||||
/**
|
||||
* 保存时使用的分类名
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val name: String
|
||||
}
|
||||
|
||||
@ -50,6 +51,7 @@ public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
|
||||
* @see LongRange Java 用户使用 [LongRange] 的构造器创建
|
||||
* @see Long.rangeTo Kotlin 用户使用 [Long.rangeTo] 创建, 如 `3000..50000`
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val autoSaveIntervalMillis: LongRange
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ internal object JarPluginLoaderImpl :
|
||||
get() = MiraiConsoleImplementationBridge.dataStorageForJarPluginLoader
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
MiraiConsole.childScopeContext(CoroutineExceptionHandler { _, throwable ->
|
||||
MiraiConsole.childScopeContext("JarPluginLoader", CoroutineExceptionHandler { _, throwable ->
|
||||
logger.error("Unhandled Jar plugin exception: ${throwable.message}", throwable)
|
||||
})
|
||||
|
||||
|
@ -26,7 +26,6 @@ import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
internal val <T> T.job: Job where T : CoroutineScope, T : Plugin get() = this.coroutineContext[Job]!!
|
||||
|
||||
@ -119,14 +118,15 @@ internal abstract class JvmPluginInternal(
|
||||
|
||||
// for future use
|
||||
@Suppress("PropertyName")
|
||||
@JvmField
|
||||
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
internal val _intrinsicCoroutineContext: CoroutineContext by lazy {
|
||||
CoroutineName("Plugin $name")
|
||||
}
|
||||
|
||||
@JvmField
|
||||
internal val coroutineContextInitializer = {
|
||||
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
||||
.plus(parentCoroutineContext)
|
||||
.plus(SupervisorJob(parentCoroutineContext[Job]))
|
||||
.plus(NamedSupervisorJob("Plugin $name", parentCoroutineContext[Job]))
|
||||
.also {
|
||||
JarPluginLoaderImpl.coroutineContext[Job]!!.invokeOnCompletion {
|
||||
this.cancel()
|
||||
@ -156,6 +156,16 @@ internal abstract class JvmPluginInternal(
|
||||
// endregion
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
internal class NamedSupervisorJob(
|
||||
private val name: String,
|
||||
parent: Job? = null
|
||||
) : CompletableJob by SupervisorJob(parent) {
|
||||
override fun toString(): String {
|
||||
return "NamedSupervisorJob($name)"
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun AtomicLong.updateWhen(condition: (Long) -> Boolean, update: (Long) -> Long): Boolean {
|
||||
while (true) {
|
||||
val current = value
|
||||
|
@ -28,7 +28,7 @@ import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsole.childScope() {
|
||||
internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsole.childScope("PluginManager") {
|
||||
|
||||
override val pluginsPath: Path = MiraiConsole.rootPath.resolve("plugins").apply { mkdir() }
|
||||
override val pluginsFolder: File = pluginsPath.toFile()
|
||||
|
@ -11,15 +11,16 @@
|
||||
|
||||
package net.mamoe.mirai.console.internal.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.plugin.NamedSupervisorJob
|
||||
import net.mamoe.mirai.console.util.BotManager
|
||||
import net.mamoe.mirai.contact.User
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
@ -52,14 +53,25 @@ internal object ManagersConfig : AutoSavePluginConfig() {
|
||||
}
|
||||
|
||||
|
||||
internal fun CoroutineContext.overrideWithSupervisorJob(): CoroutineContext = this + SupervisorJob(this[Job])
|
||||
internal fun CoroutineScope.childScope(context: CoroutineContext = EmptyCoroutineContext): CoroutineScope =
|
||||
CoroutineScope(this.childScopeContext(context))
|
||||
internal fun CoroutineContext.overrideWithSupervisorJob(name: String? = null): CoroutineContext =
|
||||
this + NamedSupervisorJob(name ?: "<unnamed>", this[Job])
|
||||
|
||||
internal fun CoroutineScope.childScopeContext(context: CoroutineContext = EmptyCoroutineContext): CoroutineContext =
|
||||
this.coroutineContext.overrideWithSupervisorJob() + context
|
||||
internal fun CoroutineScope.childScope(
|
||||
name: String? = null,
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): CoroutineScope =
|
||||
CoroutineScope(this.childScopeContext(name, context))
|
||||
|
||||
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope() {
|
||||
internal fun CoroutineScope.childScopeContext(
|
||||
name: String? = null,
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): CoroutineContext =
|
||||
this.coroutineContext.overrideWithSupervisorJob(name) + context.let {
|
||||
if (name != null) it + CoroutineName(name)
|
||||
else it
|
||||
}
|
||||
|
||||
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") {
|
||||
private val data: Array<out PluginData> = arrayOf()
|
||||
private val configs: Array<out PluginConfig> = arrayOf(ManagersConfig)
|
||||
|
||||
@ -74,13 +86,13 @@ internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope() {
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope() {
|
||||
CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginDataHolder") {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope() {
|
||||
CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginConfigHolder") {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.console.internal.util
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.future.future
|
||||
import net.mamoe.mirai.console.internal.plugin.NamedSupervisorJob
|
||||
import net.mamoe.mirai.console.plugin.jvm.JavaPluginScheduler
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@ -20,7 +21,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
internal class JavaPluginSchedulerImpl internal constructor(parentCoroutineContext: CoroutineContext) : CoroutineScope,
|
||||
JavaPluginScheduler {
|
||||
override val coroutineContext: CoroutineContext =
|
||||
parentCoroutineContext + SupervisorJob(parentCoroutineContext[Job])
|
||||
parentCoroutineContext + NamedSupervisorJob(this.toString(), parentCoroutineContext[Job])
|
||||
|
||||
override fun repeating(intervalMs: Long, runnable: Runnable): Future<Void?> {
|
||||
return this.future {
|
||||
|
@ -12,6 +12,7 @@
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import net.mamoe.mirai.console.internal.plugin.JvmPluginInternal
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
import net.mamoe.mirai.utils.secondsToMillis
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -28,5 +29,6 @@ public abstract class AbstractJvmPlugin @JvmOverloads constructor(
|
||||
) : JvmPlugin, JvmPluginInternal(parentCoroutineContext) {
|
||||
public final override val name: String get() = this.description.name
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public override val autoSaveIntervalMillis: LongRange = 30.secondsToMillis..10.minutesToMillis
|
||||
}
|
@ -11,7 +11,7 @@ package net.mamoe.mirai.console.pure
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
@ -22,71 +22,53 @@ import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.console.util.requestInput
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import org.jline.reader.UserInterruptException
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
val consoleLogger by lazy { DefaultLogger("console") }
|
||||
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
internal fun startupConsoleThread() {
|
||||
|
||||
val inputThread = thread(start = true, isDaemon = false, name = "Console Input") {
|
||||
try {
|
||||
runBlocking {
|
||||
while (true) {
|
||||
try {
|
||||
val next = MiraiConsole.requestInput("").let {
|
||||
when {
|
||||
it.startsWith(CommandManager.commandPrefix) -> it
|
||||
it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName
|
||||
else -> CommandManager.commandPrefix + it
|
||||
}
|
||||
}
|
||||
if (next.isBlank()) {
|
||||
continue
|
||||
}
|
||||
// consoleLogger.debug("INPUT> $next")
|
||||
val result = ConsoleCommandSenderImpl.executeCommand(next)
|
||||
when (result.status) {
|
||||
CommandExecuteStatus.SUCCESSFUL -> {
|
||||
}
|
||||
CommandExecuteStatus.EXECUTION_EXCEPTION -> {
|
||||
result.exception?.let(consoleLogger::error)
|
||||
}
|
||||
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
|
||||
consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助")
|
||||
}
|
||||
CommandExecuteStatus.PERMISSION_DENIED -> {
|
||||
consoleLogger.warning("Permission denied.")
|
||||
}
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
return@runBlocking
|
||||
} catch (e: CancellationException) {
|
||||
return@runBlocking
|
||||
} catch (e: UserInterruptException) {
|
||||
MiraiConsole.cancel()
|
||||
return@runBlocking
|
||||
} catch (e: Throwable) {
|
||||
consoleLogger.error("Unhandled exception", e)
|
||||
MiraiConsole.launch {
|
||||
while (true) {
|
||||
try {
|
||||
val next = MiraiConsole.requestInput("").let {
|
||||
when {
|
||||
it.startsWith(CommandManager.commandPrefix) -> it
|
||||
it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName
|
||||
else -> CommandManager.commandPrefix + it
|
||||
}
|
||||
}
|
||||
if (next.isBlank()) {
|
||||
continue
|
||||
}
|
||||
// consoleLogger.debug("INPUT> $next")
|
||||
val result = ConsoleCommandSenderImpl.executeCommand(next)
|
||||
when (result.status) {
|
||||
CommandExecuteStatus.SUCCESSFUL -> {
|
||||
}
|
||||
CommandExecuteStatus.EXECUTION_EXCEPTION -> {
|
||||
result.exception?.let(consoleLogger::error)
|
||||
}
|
||||
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
|
||||
consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助")
|
||||
}
|
||||
CommandExecuteStatus.PERMISSION_DENIED -> {
|
||||
consoleLogger.warning("Permission denied.")
|
||||
}
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
return@launch
|
||||
} catch (e: CancellationException) {
|
||||
return@launch
|
||||
} catch (e: UserInterruptException) {
|
||||
MiraiConsole.cancel()
|
||||
return@launch
|
||||
} catch (e: Throwable) {
|
||||
consoleLogger.error("Unhandled exception", e)
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
return@thread
|
||||
} catch (e: CancellationException) {
|
||||
return@thread
|
||||
} catch (e: UserInterruptException) {
|
||||
MiraiConsole.cancel()
|
||||
return@thread
|
||||
} catch (e: Throwable) {
|
||||
consoleLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
|
||||
MiraiConsole.job.invokeOnCompletion {
|
||||
runCatching {
|
||||
inputThread.interrupt()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
runCatching {
|
||||
terminal.close()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
|
@ -25,7 +25,6 @@ package net.mamoe.mirai.console.pure
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.ConsoleFrontEndImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
@ -34,6 +33,7 @@ import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.plugin.NamedSupervisorJob
|
||||
import net.mamoe.mirai.console.plugin.DeferredPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
@ -72,7 +72,7 @@ internal class MiraiConsoleImplementationPure
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
|
||||
override val configStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config"))
|
||||
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(SupervisorJob()) {
|
||||
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(NamedSupervisorJob("MiraiConsoleImplementationPure")) {
|
||||
override val mainLogger: MiraiLogger by lazy {
|
||||
MiraiConsole.newLogger("main")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user