From b2e0114e4f4a8fe1ab8bb0fb74610fe43c7233b0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 19 Feb 2020 11:28:23 +0800 Subject: [PATCH] Adjust job cancelling hence fix #65 --- .../network/QQAndroidBotNetworkHandler.kt | 11 ++++- .../commonMain/kotlin/net.mamoe.mirai/Bot.kt | 22 +++++----- .../kotlin/net.mamoe.mirai/BotImpl.kt | 43 +++++++++---------- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 4fea899ae..a8715c781 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -78,7 +78,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } catch (e: CancellationException) { return@launch } catch (e: Throwable) { - BotOfflineEvent.Dropped(bot).broadcast() + if (this@QQAndroidBotNetworkHandler.isActive) { + BotOfflineEvent.Dropped(bot).broadcast() + } return@launch } packetReceiveLock.withLock { @@ -185,6 +187,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler @UseExperimental(MiraiExperimentalAPI::class, ExperimentalTime::class) override suspend fun init(): Unit = coroutineScope { + check(bot.isActive) { "bot is dead therefore network can't init" } + check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't init" } MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect() bot.qqs.delegate.clear() @@ -494,6 +498,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler * 发送一个包, 但不期待任何返回. */ suspend fun OutgoingPacket.sendWithoutExpect() { + check(bot.isActive) { "bot is dead therefore can't send any packet" } + check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" } logger.info("Send: ${this.commandName}") withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) { channel.send(delegate) @@ -509,6 +515,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler require(timeoutMillis > 0) { "timeoutMillis must > 0" } require(retry >= 0) { "retry must >= 0" } + check(bot.isActive) { "bot is dead therefore can't send any packet" } + check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" } + var lastException: Exception? = null if (retry == 0) { val handler = PacketListener(commandName = commandName, sequenceId = sequenceId) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt index a8dea8551..96fd0db38 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -7,7 +7,7 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "FunctionName", "NOTHING_TO_INLINE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "FunctionName", "NOTHING_TO_INLINE", "UnusedImport") package net.mamoe.mirai @@ -35,7 +35,8 @@ import kotlin.jvm.JvmStatic * * 注: Bot 为全协程实现, 没有其他任务时若不使用 [join], 主线程将会退出. * - * @see Contact + * @see Contact 联系人 + * @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close]) */ @UseExperimental(MiraiInternalAPI::class) abstract class Bot : CoroutineScope { @@ -195,7 +196,9 @@ abstract class Bot : CoroutineScope { /** * 登录, 或重新登录. - * 重新登录时不会再次拉取联系人列表. + * 这个函数总是关闭一切现有网路任务, 然后重新登录并重新缓存好友列表和群列表. + * + * 一般情况下不需要重新登录. Mirai 能够自动处理掉线情况. * * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.relogin] * @@ -231,24 +234,19 @@ abstract class Bot : CoroutineScope { // endregion /** - * 关闭这个 [Bot], 停止一切相关活动. 所有引用都会被释放. + * 关闭这个 [Bot], 立即取消 [Bot] 的 [kotlinx.coroutines.SupervisorJob]. + * 之后 [kotlinx.coroutines.isActive] 将会返回 `false`. * - * 注: 不可重新登录. 必须重新实例化一个 [Bot]. + * **注意:** 不可重新登录. 必须重新实例化一个 [Bot]. * * @param cause 原因. 为 null 时视为正常关闭, 非 null 时视为异常关闭 * - * @see closeAndJoin + * @see closeAndJoin 取消并 [Bot.join], 以确保 [Bot] 相关的活动被完全关闭 */ abstract fun close(cause: Throwable? = null) // region extensions - @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("getFriend(this.toLong())")) - fun Int.qq(): QQ = getFriend(this.toLong()) - - @Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("getFriend(this)")) - fun Long.qq(): QQ = getFriend(this) - final override fun toString(): String { return "Bot(${uin})" } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt index a8686f90f..b574d7419 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt @@ -89,22 +89,17 @@ abstract class BotImpl constructor( private val offlineListener: Listener = this.subscribeAlways { event -> when (event) { is BotOfflineEvent.Dropped -> { + if (!_network.isActive) { + return@subscribeAlways + } bot.logger.info("Connection dropped or lost by server, retrying login") - var lastFailedException: Throwable? = null - repeat(configuration.reconnectionRetryTimes) { - try { - network.relogin() - logger.info("Reconnected successfully") - return@subscribeAlways - } catch (e: Throwable) { - lastFailedException = e - delay(configuration.reconnectPeriodMillis) - } - } - if (lastFailedException != null) { - throw lastFailedException!! - } + tryNTimesOrException(configuration.reconnectionRetryTimes) { + delay(configuration.reconnectPeriodMillis) + network.relogin() + logger.info("Reconnected successfully") + return@subscribeAlways + }?.let { throw it } } is BotOfflineEvent.Active -> { val msg = if (event.cause == null) { @@ -112,12 +107,12 @@ abstract class BotImpl constructor( } else { " with exception: " + event.cause.message } - bot.logger.info("Bot is closed manually$msg") - close(CancellationException(event.toString())) + bot.logger.info { "Bot is closed manually$msg" } + closeAndJoin(CancellationException(event.toString())) } is BotOfflineEvent.Force -> { - bot.logger.info("Connection occupied by another android device: ${event.message}") - close(ForceOfflineException(event.toString())) + bot.logger.info { "Connection occupied by another android device: ${event.message}" } + closeAndJoin(ForceOfflineException(event.toString())) } } } @@ -176,15 +171,19 @@ abstract class BotImpl constructor( @UseExperimental(MiraiInternalAPI::class) override fun close(cause: Throwable?) { + if (!this.botJob.isActive) { + // already cancelled + return + } kotlin.runCatching { if (cause == null) { + this.botJob.cancel() network.close() - this.botJob.complete() - offlineListener.complete() + offlineListener.cancel() } else { + this.botJob.cancel(CancellationException("bot cancelled", cause)) network.close(cause) - this.botJob.completeExceptionally(cause) - offlineListener.completeExceptionally(cause) + offlineListener.cancel(CancellationException("bot cancelled", cause)) } } groups.delegate.clear()