diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt index 942d0cf1d..a043b3a3e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt @@ -12,11 +12,14 @@ package net.mamoe.mirai.event import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import net.mamoe.mirai.JavaFriendlyAPI import net.mamoe.mirai.event.internal.broadcastInternal import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.SinceMirai import net.mamoe.mirai.utils.internal.runBlocking +import kotlin.jvm.JvmField import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic import kotlin.jvm.Volatile @@ -28,6 +31,9 @@ import kotlin.jvm.Volatile * * 所有 [Event] 都应继承 [AbstractEvent] 而不要直接实现 [Event]. 否则将无法广播也无法监听. * + * ### 广播 + * 广播事件的唯一方式为 [broadcast]. + * * @see subscribeAlways * @see subscribeOnce * @@ -53,7 +59,9 @@ interface Event { * * 当事件被 [拦截][Event.intercept] 后, 优先级较低 (靠右) 的监听器将不会被调用. * - * @see [Listener.EventPriority] 查看优先级相关信息 + * 优先级为 [Listener.EventPriority.MONITOR] 的监听器不应该调用这个函数. + * + * @see Listener.EventPriority 查看优先级相关信息 */ @SinceMirai("1.0.0") fun intercept() @@ -81,8 +89,13 @@ abstract class AbstractEvent : Event { final override val DoNotImplementThisClassButExtendAbstractEvent: Nothing get() = throw Error("Shouldn't be reached") + /** 限制一个事件实例不能并行广播. (适用于 object 广播的情况) */ + @JvmField + internal val broadCastLock = Mutex() + + @JvmField @Volatile - private var _intercepted = false + internal var _intercepted = false @Volatile private var _cancelled = false @@ -142,14 +155,24 @@ interface CancellableEvent : Event { /** * 广播一个事件的唯一途径. - * @see __broadcastJava + * + * 当事件被实现为 Kotlin `object` 时, 同一时刻只能有一个 [广播][broadcast] 存在. 较晚执行的 [广播][broadcast] 将会挂起协程并等待之前的广播任务结束. + * + * @see __broadcastJava Java 使用 */ @JvmSynthetic suspend fun <E : Event> E.broadcast(): E = apply { + check(this is AbstractEvent) { + "Events must extend AbstractEvent" + } + if (this is BroadcastControllable && !this.shouldBroadcast) { return@apply } - this@broadcast.broadcastInternal() // inline, no extra cost + this.broadCastLock.withLock { + this._intercepted = false + this.broadcastInternal() // inline, no extra cost + } } /** @@ -164,7 +187,7 @@ fun <E : Event> E.__broadcastJava(): E = apply { if (this is BroadcastControllable && !this.shouldBroadcast) { return@apply } - runBlocking { this@__broadcastJava.broadcastInternal() } + runBlocking { this@__broadcastJava.broadcast() } } /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt index ef48c9d8a..dc5147b84 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt @@ -126,13 +126,13 @@ internal expect class MiraiAtomicBoolean(initial: Boolean) { // inline: NO extra Continuation @Suppress("UNCHECKED_CAST") -internal suspend inline fun Event.broadcastInternal() { +internal suspend inline fun AbstractEvent.broadcastInternal() { if (EventDisabled) return - callAndRemoveIfRequired(this@broadcastInternal as? AbstractEvent ?: error("Events must extends AbstractEvent")) + callAndRemoveIfRequired(this@broadcastInternal) } @Suppress("DuplicatedCode") -internal suspend fun <E : AbstractEvent> callAndRemoveIfRequired( +internal suspend inline fun <E : AbstractEvent> callAndRemoveIfRequired( event: E ) { for (p in Listener.EventPriority.valuesExceptMonitor) { diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/JvmMethodListeners.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/JvmMethodListeners.kt index 397d745f1..2b5350c3b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/JvmMethodListeners.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/JvmMethodListeners.kt @@ -28,9 +28,10 @@ import kotlin.reflect.jvm.kotlinFunction * * ### Kotlin 函数 * Kotlin 函数要求: - * - 接收者和函数参数: 所标注的 Kotlin 函数必须至少拥有一个接收者或一个函数参数, 或二者都具有. 接收者和函数参数的类型必须相同 (如果二者都有) + * - 接收者 (英 receiver) 和函数参数: 所标注的 Kotlin 函数必须至少拥有一个接收者或一个函数参数, 或二者都具有. 接收者和函数参数的类型必须相同 (如果二者都存在) * 接收者或函数参数的类型都必须为 [Event] 或其子类. - * - 返回值: 为 [Unit] 或不指定返回值时将注册为 [CoroutineScope.subscribeAlways], 为 [ListeningStatus] 时将注册为 [CoroutineScope.subscribe] + * - 返回值: 为 [Unit] 或不指定返回值时将注册为 [CoroutineScope.subscribeAlways], 为 [ListeningStatus] 时将注册为 [CoroutineScope.subscribe]. + * 任何其他类型的返回值将会在注册时抛出异常. * * 所有 Kotlin 非 `suspend` 的函数都将会在 [Dispatchers.IO] 中调用 * @@ -118,8 +119,8 @@ import kotlin.reflect.jvm.kotlinFunction annotation class EventHandler( /** * 监听器优先级 - * @see Listener.EventPriority - * @see Event.intercept + * @see Listener.EventPriority 查看优先级相关信息 + * @see Event.intercept 拦截事件 */ val priority: Listener.EventPriority = EventPriority.NORMAL, /**