diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt index e37ae6495..9470fab4d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt @@ -15,7 +15,6 @@ import kotlinx.coroutines.* import net.mamoe.mirai.utils.SinceMirai import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.coroutines.suspendCoroutine import kotlin.jvm.JvmSynthetic import kotlin.reflect.KClass @@ -37,7 +36,16 @@ suspend inline fun syncFromEvent( noinline mapper: suspend E.(E) -> R? // 不要 crossinline: crossinline 后 stacktrace 会不正常 ): R { require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } - return syncFromEventOrNull(timeoutMillis, mapper) ?: error("timeout $timeoutMillis ms asyncFromEvent") + + return if (timeoutMillis == -1L) { + coroutineScope { + syncFromEventImpl(E::class, this, mapper) + } + } else { + withTimeout(timeoutMillis) { + syncFromEventImpl(E::class, this, mapper) + } + } } /** @@ -54,19 +62,13 @@ suspend inline fun syncFromEvent( @JvmSynthetic @SinceMirai("0.38.0") suspend inline fun syncFromEventOrNull( - timeoutMillis: Long = -1, + timeoutMillis: Long, noinline mapper: suspend E.(E) -> R? // 不要 crossinline: crossinline 后 stacktrace 会不正常 ): R? { - require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } + require(timeoutMillis > 0) { "timeoutMillis must be > 0" } - return if (timeoutMillis == -1L) { - coroutineScope { - syncFromEventOrNullImpl(E::class, this, mapper) - } - } else { - withTimeoutOrNull(timeoutMillis) { - syncFromEventOrNullImpl(E::class, this, mapper) - } + return withTimeoutOrNull(timeoutMillis) { + syncFromEventImpl(E::class, this, mapper) } } @@ -83,7 +85,7 @@ suspend inline fun syncFromEventOrNull( @Suppress("DeferredIsResult") @SinceMirai("0.38.0") inline fun CoroutineScope.asyncFromEventOrNull( - timeoutMillis: Long = -1, + timeoutMillis: Long, coroutineContext: CoroutineContext = EmptyCoroutineContext, noinline mapper: suspend E.(E) -> R? // 不要 crossinline: crossinline 后 stacktrace 会不正常 ): Deferred { @@ -123,11 +125,11 @@ inline fun CoroutineScope.asyncFromEvent( ////////////// @PublishedApi -internal suspend fun syncFromEventOrNullImpl( +internal suspend fun syncFromEventImpl( eventClass: KClass, coroutineScope: CoroutineScope, mapper: suspend E.(E) -> R? -): R? = suspendCoroutine { cont -> +): R = suspendCancellableCoroutine { cont -> coroutineScope.subscribe(eventClass) { cont.resumeWith(kotlin.runCatching { mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING diff --git a/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message/SubscribingGetTest.kt b/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message/SubscribingGetTest.kt new file mode 100644 index 000000000..005879adf --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message/SubscribingGetTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.message + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeout +import net.mamoe.mirai.event.TestEvent +import net.mamoe.mirai.event.broadcast +import net.mamoe.mirai.event.syncFromEvent +import net.mamoe.mirai.test.runBlocking +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine +import kotlin.test.Test +import kotlin.test.assertFailsWith + +internal class SubscribingGetTest { + + @Test + fun testSyncFromEvent(): Unit = runBlocking { + withTimeout(500) { + suspendCoroutine { cont -> + launch { + syncFromEvent(5000) { _: TestEvent -> + cont.resume(Unit) + } + } + launch { + TestEvent().broadcast() + } + } + } + } + + @Test + fun testSyncFromEventTimeout() { + runBlockingWithTimeout(500) { + assertFailsWith { + syncFromEvent(100) { _: TestEvent -> } + } + } + } +} + +internal fun runBlockingWithTimeout( + millis: Long, + context: CoroutineContext = EmptyCoroutineContext, + block: suspend CoroutineScope.() -> R +): R = runBlocking(context) { + withTimeout(millis, block) +} \ No newline at end of file