Explorar o código

Misc improvements

Him188 %!s(int64=5) %!d(string=hai) anos
pai
achega
9924d37e54

+ 2 - 1
mirai-core-qqandroid/src/jvmTest/kotlin/net/mamoe/mirai/qqandroid/utils/AtomicResizeCacheListTest.kt

@@ -15,6 +15,7 @@ import kotlin.test.Test
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 
+/*
 
 internal class AtomicResizeCacheListTest {
     @Test
@@ -38,4 +39,4 @@ internal class AtomicResizeCacheListTest {
             assertTrue { list.ensureNoDuplication(1) }
         }
     }
-}
+}*/

+ 31 - 26
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt

@@ -13,10 +13,12 @@ package net.mamoe.mirai.event
 
 import kotlinx.atomicfu.atomic
 import net.mamoe.mirai.event.internal.broadcastInternal
+import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.PlannedRemoval
 import net.mamoe.mirai.utils.SinceMirai
 import kotlin.jvm.JvmSynthetic
+import kotlin.jvm.Volatile
 
 /**
  * 可被监听的类, 可以是任何 class 或 object.
@@ -34,6 +36,20 @@ import kotlin.jvm.JvmSynthetic
  * @see [subscribe] 监听事件
  */
 interface Event {
+    /**
+     * 事件是否已被拦截.
+     *
+     * 所有事件都可以被拦截, 拦截后低优先级的监听器将不会处理到这个事件.
+     */
+    @SinceMirai("1.0.0")
+    val isIntercepted: Boolean
+
+    /**
+     * 拦截这个事件
+     */
+    @SinceMirai("1.0.0")
+    fun intercept()
+
 
     @Deprecated(
         """
@@ -47,6 +63,8 @@ interface Event {
 
 /**
  * 所有实现了 [Event] 接口的类都应该继承的父类.
+ *
+ * 在使用事件时应使用类型 [Event]. 在实现自定义事件时应继承 [AbstractEvent].
  */
 @SinceMirai("1.0.0")
 abstract class AbstractEvent : Event {
@@ -56,40 +74,20 @@ abstract class AbstractEvent : Event {
     final override val DoNotImplementThisClassButExtendAbstractEvent: Nothing
         get() = throw Error("Shouldn't be reached")
 
+    @Volatile
     private var _intercepted = false
     private val _cancelled = atomic(false)
 
-    /**
-     * 事件是否已被拦截.
-     *
-     * 所有事件都可以被拦截, 拦截后低优先级的监听器将不会处理到这个事件.
-     */
-    @SinceMirai("1.0.0")
-    val isIntercepted: Boolean
-        get() = _intercepted
+    // 实现 Event
+    override val isIntercepted: Boolean get() = _intercepted
 
-    /**
-     * 拦截这个事件.
-     * 重复拦截时不会抛出异常.
-     */
     @SinceMirai("1.0.0")
-    fun intercept() {
+    override fun intercept() {
         _intercepted = true
     }
 
-
-    /**
-     * 事件是否已取消.
-     *
-     * 事件需实现 [CancellableEvent] 接口才可以被取消,
-     * 否则此属性固定返回 false.
-     */
+    // 实现 CancellableEvent
     val isCancelled: Boolean get() = _cancelled.value
-
-    /**
-     * 取消这个事件.
-     * 重复取消时不会抛出异常.
-     */
     fun cancel() {
         check(this is CancellableEvent) {
             "Event $this is not cancellable"
@@ -103,12 +101,18 @@ abstract class AbstractEvent : Event {
  */
 interface CancellableEvent : Event {
     /**
-     * 事件是否已取消.
+     * 事件是否已被取消.
+     *
+     * 事件需实现 [CancellableEvent] 接口才可以被取消,
+     * 否则此属性固定返回 false.
      */
     val isCancelled: Boolean
 
     /**
      * 取消这个事件.
+     * 事件需实现 [CancellableEvent] 接口才可以被取消
+     *
+     * @throws IllegalStateException 当事件未实现接口 [CancellableEvent] 时抛出
      */
     fun cancel()
 }
@@ -128,6 +132,7 @@ suspend fun <E : Event> E.broadcast(): E = apply {
  * 设置为 `true` 以关闭事件.
  * 所有的 `subscribe` 都能正常添加到监听器列表, 但所有的广播都会直接返回.
  */
+@MiraiExperimentalAPI
 var EventDisabled = false
 
 /**

+ 52 - 42
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt

@@ -14,14 +14,17 @@ import kotlinx.coroutines.*
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import net.mamoe.mirai.event.*
-import net.mamoe.mirai.utils.*
+import net.mamoe.mirai.event.events.BotEvent
+import net.mamoe.mirai.utils.LockFreeLinkedList
+import net.mamoe.mirai.utils.MiraiExperimentalAPI
+import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.MiraiLogger
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.coroutineContext
 import kotlin.jvm.JvmField
 import kotlin.reflect.KClass
 
 
-@PublishedApi
 internal fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L {
     with(GlobalEventListeners[listener.priority]) {
         @Suppress("UNCHECKED_CAST")
@@ -37,7 +40,6 @@ internal fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listen
 }
 
 
-@PublishedApi
 @Suppress("FunctionName")
 internal fun <E : Event> CoroutineScope.Handler(
     coroutineContext: CoroutineContext,
@@ -53,9 +55,7 @@ internal fun <E : Event> CoroutineScope.Handler(
 /**
  * 事件处理器.
  */
-@PublishedApi
-internal class Handler<in E : Event>
-@PublishedApi internal constructor(
+internal class Handler<in E : Event> internal constructor(
     parentJob: Job?,
     subscriberContext: CoroutineContext,
     @JvmField val handler: suspend (E) -> ListeningStatus,
@@ -65,8 +65,6 @@ internal class Handler<in E : Event>
 
     private val subscriberContext: CoroutineContext = subscriberContext + this // override Job.
 
-
-    @MiraiInternalAPI
     val lock: Mutex? = when (concurrencyKind) {
         Listener.ConcurrencyKind.LOCKED -> Mutex()
         else -> null
@@ -84,10 +82,11 @@ internal class Handler<in E : Event>
                 ?: coroutineContext[CoroutineExceptionHandler]?.handleException(subscriberContext, e)
                 ?: kotlin.run {
                     @Suppress("DEPRECATION")
-                    MiraiLogger.warning(
-                        """Event processing: An exception occurred but no CoroutineExceptionHandler found, 
+                    (if (event is BotEvent) event.bot.logger else MiraiLogger)
+                        .warning(
+                            """Event processing: An exception occurred but no CoroutineExceptionHandler found, 
                         either in coroutineContext from Handler job, or in subscriberContext""".trimIndent(), e
-                    )
+                        )
                 }
             // this.complete() // do not `completeExceptionally`, otherwise parentJob will fai`l.
             // ListeningStatus.STOPPED
@@ -102,6 +101,7 @@ internal class ListenerNode(
     val listener: Listener<Event>,
     val owner: KClass<out Event>
 )
+
 internal expect object GlobalEventListeners {
     operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerNode>
 }
@@ -116,51 +116,61 @@ internal expect class MiraiAtomicBoolean(initial: Boolean) {
 
 // inline: NO extra Continuation
 @Suppress("UNCHECKED_CAST")
-internal suspend inline fun Event.broadcastInternal() = coroutineScope {
-    if (EventDisabled) return@coroutineScope
+internal suspend inline fun Event.broadcastInternal() {
+    @OptIn(MiraiExperimentalAPI::class)
+    if (EventDisabled) return
     callAndRemoveIfRequired(this@broadcastInternal as? AbstractEvent ?: error("Events must extends AbstractEvent"))
 }
 
+@Suppress("DuplicatedCode")
 @OptIn(MiraiInternalAPI::class)
-private suspend fun <E : AbstractEvent> callAndRemoveIfRequired(
+internal suspend fun <E : AbstractEvent> callAndRemoveIfRequired(
     event: E
 ) {
-    coroutineScope {
-        for (p in Listener.EventPriority.values()) {
-            GlobalEventListeners[p].forEachNode { eventNode ->
-                if (event.isIntercepted) {
-                    return@coroutineScope
+    for (p in Listener.EventPriority.valuesExceptMonitor) {
+        GlobalEventListeners[p].forEachNode { eventNode ->
+            if (event.isIntercepted) {
+                return
+            }
+            val node = eventNode.nodeValue
+            if (!node.owner.isInstance(event)) return@forEachNode
+            val listener = node.listener
+            when (listener.concurrencyKind) {
+                Listener.ConcurrencyKind.LOCKED -> {
+                    (listener as Handler).lock!!.withLock {
+                        if (listener.onEvent(event) == ListeningStatus.STOPPED) {
+                            removeNode(eventNode)
+                        }
+                    }
+                }
+                Listener.ConcurrencyKind.CONCURRENT -> {
+                    if (listener.onEvent(event) == ListeningStatus.STOPPED) {
+                        removeNode(eventNode)
+                    }
                 }
-                val node = eventNode.nodeValue
-                if (!node.owner.isInstance(event)) return@forEachNode
-                val listener = node.listener
+            }
+        }
+    }
+    coroutineScope {
+        GlobalEventListeners[Listener.EventPriority.MONITOR].forEachNode { eventNode ->
+            if (event.isIntercepted) {
+                return@coroutineScope
+            }
+            val node = eventNode.nodeValue
+            if (!node.owner.isInstance(event)) return@forEachNode
+            val listener = node.listener
+            launch {
                 when (listener.concurrencyKind) {
                     Listener.ConcurrencyKind.LOCKED -> {
                         (listener as Handler).lock!!.withLock {
-                            kotlin.runCatching {
-                                when (listener.onEvent(event)) {
-                                    ListeningStatus.STOPPED -> {
-                                        removeNode(eventNode)
-                                    }
-                                    else -> {
-                                    }
-                                }
-                            }.onFailure {
-                                // TODO("Exception catching")
+                            if (listener.onEvent(event) == ListeningStatus.STOPPED) {
+                                removeNode(eventNode)
                             }
                         }
                     }
                     Listener.ConcurrencyKind.CONCURRENT -> {
-                        kotlin.runCatching {
-                            when (listener.onEvent(event)) {
-                                ListeningStatus.STOPPED -> {
-                                    removeNode(eventNode)
-                                }
-                                else -> {
-                                }
-                            }
-                        }.onFailure {
-                            // TODO("Exception catching")
+                        if (listener.onEvent(event) == ListeningStatus.STOPPED) {
+                            removeNode(eventNode)
                         }
                     }
                 }

+ 140 - 70
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt

@@ -7,22 +7,28 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:Suppress("unused")
+
 package net.mamoe.mirai.event
 
 import kotlinx.coroutines.CompletableJob
 import kotlinx.coroutines.CoroutineExceptionHandler
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.sync.Mutex
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.Listener.EventPriority.*
 import net.mamoe.mirai.event.events.BotEvent
 import net.mamoe.mirai.event.internal.Handler
 import net.mamoe.mirai.event.internal.subscribeInternal
 import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.MiraiLogger
+import net.mamoe.mirai.utils.PlannedRemoval
 import net.mamoe.mirai.utils.SinceMirai
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.jvm.JvmName
+import kotlin.jvm.JvmStatic
 import kotlin.jvm.JvmSynthetic
 import kotlin.reflect.KClass
 
@@ -40,7 +46,12 @@ enum class ListeningStatus {
     LISTENING,
 
     /**
-     * 表示已停止
+     * 表示已停止.
+     *
+     * - 若监听器使用 [Listener.ConcurrencyKind.LOCKED],
+     * 在这之后监听器将会被从监听器列表中删除, 因此不再能接收到事件.
+     * - 若使用 [Listener.ConcurrencyKind.CONCURRENT],
+     * 在这之后无法保证立即停止监听.
      */
     STOPPED
 }
@@ -70,15 +81,45 @@ interface Listener<in E : Event> : CompletableJob {
      */
     val concurrencyKind: ConcurrencyKind
 
+    /**
+     * 事件优先级.
+     *
+     * 在广播时, 事件监听器的调用顺序为 (从左到右):
+     * `[HIGHEST]` -> `[HIGH]` -> `[NORMAL]` -> `[LOW]` -> `[LOWEST]` -> `[MONITOR]`
+     *
+     * - 使用 [MONITOR] 优先级的监听器将会被**并行**调用.
+     * - 使用其他优先级的监听器都将会**按顺序**调用.
+     *   因此一个监听器的挂起可以阻塞事件处理过程而导致低优先级的监听器较晚处理.
+     */
+    @SinceMirai("1.0.0")
     enum class EventPriority {
-        MONITOR, HIGHEST, HIGH, NORMAL, LOW, LOWEST
 
+        HIGHEST, HIGH, NORMAL, LOW, LOWEST,
+
+        /**
+         * 最低的优先级.
+         *
+         * 只监听事件而不拦截事件的监听器应使用此监听器.
+         */
+        MONITOR;
+
+        companion object {
+            @JvmStatic
+            internal val valuesExceptMonitor: Array<EventPriority> = arrayOf(HIGHEST, HIGH, NORMAL, LOW, LOWEST)
+        }
     }
 
-    val priority: EventPriority get() = EventPriority.NORMAL
+    /**
+     * 事件优先级
+     * @see [EventPriority]
+     */
+    val priority: EventPriority get() = NORMAL
 
+    /**
+     * 这个方法将会调用 [subscribe] 时提供的参数 `noinline handler: suspend E.(E) -> ListeningStatus`.
+     * 并捕捉其异常.
+     */
     suspend fun onEvent(event: E): ListeningStatus
-
 }
 
 // region 顶层方法 创建当前 coroutineContext 下的子 Job
@@ -87,50 +128,59 @@ interface Listener<in E : Event> : CompletableJob {
  * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
  * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行.
  *
- * 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听.
- * 或 [Listener.complete] 后结束.
- *
- * 这个函数返回 [Listener], 它是一个 [CompletableJob]. 请注意它除非被 [Listener.complete] 或 [Listener.cancel], 则不会完成.
- * 例:
- * ```kotlin
- * runBlocking { // this: CoroutineScope
- *   subscribe<Event> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
- * }
- * foo()
- * ```
- * `runBlocking` 不会结束, 也就是下一行 `foo()` 不会被执行. 直到监听时创建的 `Listener` 被停止.
  *
+ * ### 创建监听
  *
+ * #### 单个 [Bot] 的事件监听
  * 要创建一个仅在某个机器人在线时的监听, 请在 [Bot] 下调用本函数 (因为 [Bot] 也实现 [CoroutineScope]).
  * 这种方式创建的监听会自动筛选 [Bot].
- * ```kotlin
+ * ```
  * bot1.subscribe<BotEvent> { /* 只会处理来自 bot1 的事件 */ }
  * ```
  *
- *
- * 要创建一个全局都存在的监听(不推荐), 即守护协程, 请在 [GlobalScope] 下调用本函数:
- * ```kotlin
+ * #### 全局范围监听
+ * 要创建一个全局都存在的监听(不推荐), 请使用除 [Bot] 外的任意 [协程作用域][CoroutineScope] 调用本函数:
+ * ```
  * GlobalScope.subscribe<Event> { /* 会收到来自全部 Bot 的事件和与 Bot 不相关的事件 */ }
  * ```
  *
  *
+ * ### 异常处理
  * 事件处理时的 [CoroutineContext] 为调用本函数时的 [receiver][this] 的 [CoroutineScope.coroutineContext].
  * 因此:
- * - 事件处理时抛出的异常将会在 [this] 的 [CoroutineExceptionHandler] 中处理
- *   若 [this] 没有 [CoroutineExceptionHandler], 则在事件广播方的 [CoroutineExceptionHandler] 处理
- *   若均找不到, 则会触发 logger warning.
+ * - 当参数 [handler] 处理抛出异常时, 将会按如下顺序寻找 [CoroutineExceptionHandler] 处理异常:
+ *   1. 参数 [coroutineContext]
+ *   2. 接收者 [this] 的 [CoroutineScope.coroutineContext]
+ *   3. [Event.broadcast] 调用者的 [coroutineContext]
+ *   4. 若事件为 [BotEvent], 则从 [BotEvent.bot] 获取到 [Bot], 进而在 [Bot.coroutineContext] 中寻找
+ *   5. 若以上四个步骤均无法获取 [CoroutineExceptionHandler], 则使用 [MiraiLogger.Companion] 通过日志记录. 但这种情况理论上不应发生.
  * - 事件处理时抛出异常不会停止监听器.
  * - 建议在事件处理中 (即 [handler] 里) 处理异常,
- *   或在 [this] 的 [CoroutineScope.coroutineContext] 中添加 [CoroutineExceptionHandler].
+ *   或在参数 [coroutineContext] 中添加 [CoroutineExceptionHandler].
+ *
+ *
+ * ### 停止监听
+ * 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听.
+ * 或 [Listener.complete] 后结束.
+ *
+ * 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [CoroutineScope] 的一个 [子任务][Job]
+ * 例:
+ * ```
+ * runBlocking { // this: CoroutineScope
+ *   subscribe<Event> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
+ * }
+ * foo()
+ * ```
+ * `runBlocking` 不会结束, 也就是下一行 `foo()` 不会被执行. 直到监听时创建的 `Listener` 被停止.
  *
  *
- * **注意:** 事件处理是 `suspend` 的, 请规范处理 JVM 阻塞方法.
  *
  * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext].
  * @param concurrency 并发类型. 查看 [Listener.ConcurrencyKind]
  * @param priority  监听优先级,优先级越高越先执行
+ * @param handler 事件处理器. 在接收到事件时会调用这个处理器. 其返回值意义参考 [ListeningStatus]. 其异常处理参考上文
  *
- * @see syncFromEvent 监听一个事件, 并尝试从这个事件中获取一个值.
+ * @see syncFromEvent 挂起当前协程, 监听一个事件, 并尝试从这个事件中**同步**一个值
  * @see asyncFromEvent 异步监听一个事件, 并尝试从这个事件中获取一个值.
  *
  * @see selectMessages 以 `when` 的语法 '选择' 即将到来的一条消息.
@@ -142,15 +192,18 @@ interface Listener<in E : Event> : CompletableJob {
  * @see subscribeMessages       监听消息 DSL
  * @see subscribeGroupMessages  监听群消息 DSL
  * @see subscribeFriendMessages 监听好友消息 DSL
+ * @see subscribeTempMessages   监听临时会话消息 DSL
  */
 inline fun <reified E : Event> CoroutineScope.subscribe(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.LOCKED,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     noinline handler: suspend E.(E) -> ListeningStatus
 ): Listener<E> = subscribe(E::class, coroutineContext, concurrency, priority, handler)
 
 /**
+ * 与 [subscribe] 的区别是接受 [eventClass] 参数, 而不使用 `reified` 泛型
+ *
  * @see CoroutineScope.subscribe
  */
 @SinceMirai("0.38.0")
@@ -158,7 +211,7 @@ fun <E : Event> CoroutineScope.subscribe(
     eventClass: KClass<E>,
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.LOCKED,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     handler: suspend E.(E) -> ListeningStatus
 ): Listener<E> = eventClass.subscribeInternal(Handler(coroutineContext, concurrency, priority) { it.handler(it); })
 
@@ -167,7 +220,7 @@ fun <E : Event> CoroutineScope.subscribe(
  * 每当 [事件广播][Event.broadcast] 时, [listener] 都会被执行.
  *
  * 可在任意时候通过 [Listener.complete] 来主动停止监听.
- * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ * [CoroutineScope] 被关闭后事件监听会被 [取消][Listener.cancel].
  *
  * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
  * @param priority 处理优先级, 优先级高的先执行
@@ -177,7 +230,7 @@ fun <E : Event> CoroutineScope.subscribe(
 inline fun <reified E : Event> CoroutineScope.subscribeAlways(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.LOCKED,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     noinline listener: suspend E.(E) -> Unit
 ): Listener<E> = subscribeAlways(E::class, coroutineContext, concurrency, priority, listener)
 
@@ -190,7 +243,7 @@ fun <E : Event> CoroutineScope.subscribeAlways(
     eventClass: KClass<E>,
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.LOCKED,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     listener: suspend E.(E) -> Unit
 ): Listener<E> = eventClass.subscribeInternal(
     Handler(coroutineContext, concurrency, priority) { it.listener(it); ListeningStatus.LISTENING }
@@ -201,7 +254,7 @@ fun <E : Event> CoroutineScope.subscribeAlways(
  * 仅在第一次 [事件广播][Event.broadcast] 时, [listener] 会被执行.
  *
  * 可在任意时候通过 [Listener.complete] 来主动停止监听.
- * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ * [CoroutineScope] 被关闭后事件监听会被 [取消][Listener.cancel].
  *
  * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
  * @param priority 处理优先级, 优先级高的先执行
@@ -211,7 +264,7 @@ fun <E : Event> CoroutineScope.subscribeAlways(
 @JvmSynthetic
 inline fun <reified E : Event> CoroutineScope.subscribeOnce(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     noinline listener: suspend E.(E) -> Unit
 ): Listener<E> = subscribeOnce(E::class, coroutineContext, priority, listener)
 
@@ -222,7 +275,7 @@ inline fun <reified E : Event> CoroutineScope.subscribeOnce(
 fun <E : Event> CoroutineScope.subscribeOnce(
     eventClass: KClass<E>,
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     listener: suspend E.(E) -> Unit
 ): Listener<E> = eventClass.subscribeInternal(
     Handler(coroutineContext, Listener.ConcurrencyKind.LOCKED, priority) { it.listener(it); ListeningStatus.STOPPED }
@@ -234,17 +287,10 @@ fun <E : Event> CoroutineScope.subscribeOnce(
 
 
 /**
- * 在 [Bot] 的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
- * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行,
- * 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听
- *
- * 可在任意时候通过 [Listener.complete] 来主动停止监听.
- * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
- *
- * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
- * @param priority 事件优先级, 优先级高的先处理
+ * **注意:** 与 [CoroutineScope.subscribe] 不同的是,
+ * [Bot.subscribe] 会筛选事件来源 [Bot], 只监听来自 [this] 的事件.
  *
- * @see subscribe 获取更多说明
+ * @see CoroutineScope.subscribe 获取更多说明
  */
 @JvmSynthetic
 @JvmName("subscribeAlwaysForBot")
@@ -252,11 +298,14 @@ fun <E : Event> CoroutineScope.subscribeOnce(
 inline fun <reified E : BotEvent> Bot.subscribe(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.LOCKED,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     noinline handler: suspend E.(E) -> ListeningStatus
 ): Listener<E> = this.subscribe(E::class, coroutineContext, concurrency, priority, handler)
 
 /**
+ * **注意:** 与 [CoroutineScope.subscribe] 不同的是,
+ * [Bot.subscribe] 会筛选事件来源 [Bot], 只监听来自 [this] 的事件.
+ *
  * @see Bot.subscribe
  */
 @SinceMirai("0.38.0")
@@ -264,7 +313,7 @@ fun <E : BotEvent> Bot.subscribe(
     eventClass: KClass<E>,
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.LOCKED,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     handler: suspend E.(E) -> ListeningStatus
 ): Listener<E> = eventClass.subscribeInternal(
     Handler(
@@ -275,16 +324,10 @@ fun <E : BotEvent> Bot.subscribe(
 )
 
 /**
- * 在 [Bot] 的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
- * 每当 [事件广播][Event.broadcast] 时, [listener] 都会被执行.
- *
- * 可在任意时候通过 [Listener.complete] 来主动停止监听.
- * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
- *
- * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
- * @param priority 事件优先级, 优先级高的先处理
+ * **注意:** 与 [CoroutineScope.subscribe] 不同的是,
+ * [Bot.subscribe] 会筛选事件来源 [Bot], 只监听来自 [this] 的事件.
  *
- * @see subscribe 获取更多说明
+ * @see CoroutineScope.subscribeAlways 获取更多说明
  */
 @JvmSynthetic
 @JvmName("subscribeAlwaysForBot1")
@@ -292,11 +335,14 @@ fun <E : BotEvent> Bot.subscribe(
 inline fun <reified E : BotEvent> Bot.subscribeAlways(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     noinline listener: suspend E.(E) -> Unit
 ): Listener<E> = subscribeAlways(E::class, coroutineContext, concurrency, priority, listener)
 
 /**
+ * **注意:** 与 [CoroutineScope.subscribe] 不同的是,
+ * [Bot.subscribe] 会筛选事件来源 [Bot], 只监听来自 [this] 的事件.
+ *
  * @see Bot.subscribeAlways
  */
 @SinceMirai("0.38.0")
@@ -304,40 +350,37 @@ fun <E : BotEvent> Bot.subscribeAlways(
     eventClass: KClass<E>,
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     concurrency: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     listener: suspend E.(E) -> Unit
 ): Listener<E> = eventClass.subscribeInternal(
     Handler(coroutineContext, concurrency, priority) { if (it.bot === this) it.listener(it); ListeningStatus.LISTENING }
 )
 
 /**
- * 在 [Bot] 的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
- * 仅在第一次 [事件广播][Event.broadcast] 时, [listener] 会被执行.
- *
- * 可在任意时候通过 [Listener.complete] 来主动停止监听.
- * [Bot] 被关闭后事件监听会被 [取消][Listener.cancel].
+ * **注意:** 与 [CoroutineScope.subscribe] 不同的是,
+ * [Bot.subscribe] 会筛选事件来源 [Bot], 只监听来自 [this] 的事件.
  *
- * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext]
- * @param priority 事件优先级, 高的先处理
- *
- * @see subscribe 获取更多说明
+ * @see subscribeOnce 获取更多说明
  */
 @JvmSynthetic
 @JvmName("subscribeOnceForBot2")
 inline fun <reified E : BotEvent> Bot.subscribeOnce(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     noinline listener: suspend E.(E) -> Unit
 ): Listener<E> = subscribeOnce(E::class, coroutineContext, priority, listener)
 
 /**
+ * **注意:** 与 [CoroutineScope.subscribe] 不同的是,
+ * [Bot.subscribe] 会筛选事件来源 [Bot], 只监听来自 [this] 的事件.
+ *
  * @see Bot.subscribeOnce
  */
 @SinceMirai("0.38.0")
 fun <E : BotEvent> Bot.subscribeOnce(
     eventClass: KClass<E>,
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
-    priority: Listener.EventPriority = Listener.EventPriority.NORMAL,
+    priority: Listener.EventPriority = NORMAL,
     listener: suspend E.(E) -> Unit
 ): Listener<E> =
     eventClass.subscribeInternal(Handler(coroutineContext, Listener.ConcurrencyKind.LOCKED, priority) {
@@ -351,6 +394,7 @@ fun <E : BotEvent> Bot.subscribeOnce(
 
 // region 为了兼容旧版本的方法
 
+@PlannedRemoval("1.2.0")
 @JvmName("subscribe")
 @JvmSynthetic
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -362,9 +406,11 @@ inline fun <reified E : Event> CoroutineScope.subscribeDeprecated(
 ): Listener<E> = subscribe(
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     handler = handler
 )
 
+@PlannedRemoval("1.2.0")
 @JvmName("subscribe")
 @JvmSynthetic
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -378,9 +424,11 @@ fun <E : Event> CoroutineScope.subscribeDeprecated(
     eventClass = eventClass,
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     handler = handler
 )
 
+@PlannedRemoval("1.2.0")
 @JvmName("subscribeAlways")
 @JvmSynthetic
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -392,9 +440,11 @@ inline fun <reified E : Event> CoroutineScope.subscribeAlwaysDeprecated(
 ): Listener<E> = subscribeAlways(
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     listener = listener
 )
 
+@PlannedRemoval("1.2.0")
 @JvmName("subscribeAlways")
 @JvmSynthetic
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -408,9 +458,11 @@ fun <E : Event> CoroutineScope.subscribeAlwaysDeprecated(
     eventClass = eventClass,
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     listener = listener
 )
 
+@PlannedRemoval("1.2.0")
 @JvmName("subscribeOnce")
 @JvmSynthetic
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -418,8 +470,13 @@ fun <E : Event> CoroutineScope.subscribeAlwaysDeprecated(
 inline fun <reified E : Event> CoroutineScope.subscribeOnceDeprecated(
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
     noinline listener: suspend E.(E) -> Unit
-): Listener<E> = subscribeOnce(coroutineContext = coroutineContext, listener = listener)
+): Listener<E> = subscribeOnce(
+    coroutineContext = coroutineContext,
+    priority = MONITOR,
+    listener = listener
+)
 
+@PlannedRemoval("1.2.0")
 @JvmName("subscribeOnce")
 @JvmSynthetic
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -431,9 +488,11 @@ fun <E : Event> CoroutineScope.subscribeOnceDeprecated(
 ): Listener<E> = subscribeOnce(
     eventClass = eventClass,
     coroutineContext = coroutineContext,
+    priority = MONITOR,
     listener = listener
 )
 
+@PlannedRemoval("1.2.0")
 @JvmSynthetic
 @JvmName("subscribeAlwaysForBot")
 @OptIn(MiraiInternalAPI::class)
@@ -446,9 +505,11 @@ inline fun <reified E : BotEvent> Bot.subscribeDeprecated(
 ): Listener<E> = this.subscribe(
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     handler = handler
 )
 
+@PlannedRemoval("1.2.0")
 @JvmSynthetic
 @JvmName("subscribe")
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -462,9 +523,11 @@ fun <E : BotEvent> Bot.subscribeDeprecated(
     eventClass = eventClass,
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     handler = handler
 )
 
+@PlannedRemoval("1.2.0")
 @JvmSynthetic
 @JvmName("subscribeAlwaysForBot1")
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -477,9 +540,11 @@ inline fun <reified E : BotEvent> Bot.subscribeAlwaysDeprecated(
 ): Listener<E> = subscribeAlways(
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     listener = listener
 )
 
+@PlannedRemoval("1.2.0")
 @JvmSynthetic
 @JvmName("subscribeAlways")
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -493,9 +558,11 @@ fun <E : BotEvent> Bot.subscribeAlwaysDeprecated(
     eventClass = eventClass,
     coroutineContext = coroutineContext,
     concurrency = concurrency,
+    priority = MONITOR,
     listener = listener
 )
 
+@PlannedRemoval("1.2.0")
 @JvmSynthetic
 @JvmName("subscribeOnceForBot2")
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -505,9 +572,11 @@ inline fun <reified E : BotEvent> Bot.subscribeOnceDeprecated(
     noinline listener: suspend E.(E) -> Unit
 ): Listener<E> = subscribeOnce(
     coroutineContext = coroutineContext,
+    priority = MONITOR,
     listener = listener
 )
 
+@PlannedRemoval("1.2.0")
 @JvmSynthetic
 @JvmName("subscribeOnce")
 @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@@ -519,6 +588,7 @@ fun <E : BotEvent> Bot.subscribeOnceDeprecated(
 ): Listener<E> = subscribeOnce(
     eventClass = eventClass,
     coroutineContext = coroutineContext,
+    priority = MONITOR,
     listener = listener
 )
 // endregion

+ 18 - 20
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/event/EventTests.kt

@@ -9,8 +9,6 @@
 
 package net.mamoe.mirai.event
 
-import kotlinx.atomicfu.AtomicInt
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.*
 import net.mamoe.mirai.event.internal.GlobalEventListeners
 import net.mamoe.mirai.utils.MiraiInternalAPI
@@ -18,7 +16,6 @@ import net.mamoe.mirai.utils.StepUtil
 import net.mamoe.mirai.utils.internal.runBlocking
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
-import kotlin.concurrent.thread
 import kotlin.test.Test
 import kotlin.test.assertTrue
 
@@ -67,7 +64,6 @@ class EventTests {
         }
         kotlinx.coroutines.runBlocking {
             ParentEvent().broadcast()
-            delay(5000L) // ?
         }
         val called = counter.get()
         println("Registered $listeners listeners and $called called")
@@ -109,37 +105,39 @@ class EventTests {
     }
 
     @Test
-    fun `test concurrent listening 2`() {
+    fun `test concurrent listening 2`() = runBlocking {
         resetEventListeners()
         val registered = AtomicInteger()
         val called = AtomicInteger()
-        val threads = mutableListOf<Thread>()
-        repeat(50) {
-            threads.add(thread {
-                repeat(444) {
-                    registered.getAndIncrement()
-                    GlobalScope.launch {
-                        subscribeAlways<ParentEvent> {
+
+        val supervisor = CoroutineScope(SupervisorJob())
+
+        coroutineScope {
+            repeat(50) {
+                launch {
+                    repeat(444) {
+                        registered.getAndIncrement()
+
+                        supervisor.subscribeAlways<ParentEvent> {
                             called.getAndIncrement()
                         }
                     }
                 }
-            })
-        }
-        Thread.sleep(5000L)// Wait all thread started.
-        threads.forEach {
-            it.join() // Wait all finished
+            }
         }
+
         println("All listeners registered")
+
         val postCount = 3
-        kotlinx.coroutines.runBlocking {
+        coroutineScope {
             repeat(postCount) {
-                ParentEvent().broadcast()
+                launch { ParentEvent().broadcast() }
             }
-            delay(5000L)
         }
+
         val calledCount = called.get()
         val shouldCalled = registered.get() * postCount
+
         println("Should call $shouldCalled times and $called called")
         if (shouldCalled != calledCount) {
             throw IllegalStateException("?")