From 7dd4a9669bcc9ba246075fdd3acb36fcaa5d5174 Mon Sep 17 00:00:00 2001
From: Karlatemp <karlatemp@vip.qq.com>
Date: Thu, 12 Nov 2020 12:38:49 +0800
Subject: [PATCH] Fix console stopping, fix #221

- Make StopCommand async
- Close terminal reader only
---
 .../src/command/BuiltInCommands.kt            | 46 ++++++++++---------
 backend/mirai-console/src/util/SemVersion.kt  |  4 +-
 .../src/ConsoleThread.kt                      | 10 ++--
 3 files changed, 29 insertions(+), 31 deletions(-)

diff --git a/backend/mirai-console/src/command/BuiltInCommands.kt b/backend/mirai-console/src/command/BuiltInCommands.kt
index c24fe9948..a5d978b53 100644
--- a/backend/mirai-console/src/command/BuiltInCommands.kt
+++ b/backend/mirai-console/src/command/BuiltInCommands.kt
@@ -102,29 +102,31 @@ public object BuiltInCommands {
 
         @Handler
         public suspend fun CommandSender.handle() {
-            kotlin.runCatching {
-                closingLock.withLock {
-                    sendMessage("Stopping mirai-console")
-                    kotlin.runCatching {
-                        runIgnoreException<CancellationException> { MiraiConsole.job.cancelAndJoin() }
-                    }.fold(
-                        onSuccess = {
-                            runIgnoreException<EventCancelledException> { sendMessage("mirai-console stopped successfully.") }
-                        },
-                        onFailure = {
-                            if (it is CancellationException) return@fold
-                            @OptIn(ConsoleInternalApi::class)
-                            MiraiConsole.mainLogger.error("Exception in stop", it)
-                            runIgnoreException<EventCancelledException> {
-                                sendMessage(
-                                    it.localizedMessage ?: it.message ?: it.toString()
-                                )
+            GlobalScope.launch {
+                kotlin.runCatching {
+                    closingLock.withLock {
+                        if (!MiraiConsole.isActive) return@withLock
+                        sendMessage("Stopping mirai-console")
+                        kotlin.runCatching {
+                            MiraiConsole.job.cancelAndJoin()
+                        }.fold(
+                            onSuccess = {
+                                runIgnoreException<EventCancelledException> { sendMessage("mirai-console stopped successfully.") }
+                            },
+                            onFailure = {
+                                @OptIn(ConsoleInternalApi::class)
+                                MiraiConsole.mainLogger.error("Exception in stop", it)
+                                runIgnoreException<EventCancelledException> {
+                                    sendMessage(
+                                        it.localizedMessage ?: it.message ?: it.toString()
+                                    )
+                                }
                             }
-                        }
-                    )
-                }
-            }.exceptionOrNull()?.let(MiraiConsole.mainLogger::error)
-            exitProcess(0)
+                        )
+                    }
+                }.exceptionOrNull()?.let(MiraiConsole.mainLogger::error)
+                exitProcess(0)
+            }
         }
     }
 
diff --git a/backend/mirai-console/src/util/SemVersion.kt b/backend/mirai-console/src/util/SemVersion.kt
index f350c0f58..4aee3861d 100644
--- a/backend/mirai-console/src/util/SemVersion.kt
+++ b/backend/mirai-console/src/util/SemVersion.kt
@@ -94,9 +94,7 @@ internal constructor(
         private val impl = SemVersionInternal.parseRangeRequirement(rule)
 
         /** 在 [version] 满足此要求时返回 true */
-        public fun test(version: SemVersion): Boolean {
-            return impl.test(version)
-        }
+        public fun test(version: SemVersion): Boolean = impl.test(version)
 
         public object RequirementAsStringSerializer : KSerializer<Requirement> by String.serializer().map(
             serializer = { it.rule },
diff --git a/frontend/mirai-console-terminal/src/ConsoleThread.kt b/frontend/mirai-console-terminal/src/ConsoleThread.kt
index 97294ca1c..2e2e78fba 100644
--- a/frontend/mirai-console-terminal/src/ConsoleThread.kt
+++ b/frontend/mirai-console-terminal/src/ConsoleThread.kt
@@ -9,10 +9,7 @@
 
 package net.mamoe.mirai.console.terminal
 
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.*
 import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.command.*
 import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
@@ -30,12 +27,13 @@ internal fun startupConsoleThread() {
     if (terminal is NoConsole) return
 
     MiraiConsole.launch(CoroutineName("Input Cancelling Daemon")) {
-        while (true) {
+        while (isActive) {
             delay(2000)
         }
     }.invokeOnCompletion {
         runCatching<Unit> {
-            terminal.close()
+            // 应该仅关闭用户输入
+            terminal.reader().shutdown()
             ConsoleInputImpl.thread.shutdownNow()
             runCatching {
                 ConsoleInputImpl.executingCoroutine?.cancel(EndOfFileException())