Extract sync logic

This commit is contained in:
Him188 2021-04-25 12:00:30 +08:00
parent 076cd4997d
commit f9ddf74d8e
6 changed files with 38 additions and 109 deletions

View File

@ -63,6 +63,22 @@ public fun CoroutineScope.childScope(
public fun CoroutineContext.childScope(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
): CoroutineScope {
val ctx = this + coroutineContext
return CoroutineScope(ctx + SupervisorJob(ctx.job))
return CoroutineScope(this.childScopeContext(coroutineContext))
}
/**
* Creates a child scope of the receiver context scope.
*/
public fun CoroutineContext.childScopeContext(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
): CoroutineContext {
val ctx = this + coroutineContext
return ctx + SupervisorJob(ctx.job)
}
public inline fun <E : U, U : CoroutineContext.Element> CoroutineContext.getOrElse(
key: CoroutineContext.Key<E>,
default: () -> U
): U {
return this[key] ?: default()
}

View File

@ -18,27 +18,24 @@
package net.mamoe.mirai.internal
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.ConcurrencyKind
import net.mamoe.mirai.event.EventChannel
import net.mamoe.mirai.event.EventPriority.MONITOR
import net.mamoe.mirai.event.GlobalEventChannel
import net.mamoe.mirai.event.Listener
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.supervisorJob
import net.mamoe.mirai.utils.*
import kotlin.coroutines.CoroutineContext
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
/**
* Protocol-irrelevant implementations
*/
internal abstract class AbstractBot constructor(
final override val configuration: BotConfiguration,
final override val id: Long,
@ -48,20 +45,17 @@ internal abstract class AbstractBot constructor(
///////////////////////////////////////////////////////////////////////////
// FASTEST INIT
private val supervisor = SupervisorJob(configuration.parentCoroutineContext[Job])
final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) }
final override val coroutineContext: CoroutineContext = // for id
configuration.parentCoroutineContext
.plus(supervisor)
.plus(
configuration.parentCoroutineContext[CoroutineExceptionHandler]
?: CoroutineExceptionHandler { _, e ->
final override val coroutineContext: CoroutineContext = configuration.parentCoroutineContext.childScopeContext(
configuration.parentCoroutineContext.getOrElse(CoroutineExceptionHandler) {
CoroutineExceptionHandler { _, e ->
logger.error("An exception was thrown under a coroutine of Bot", e)
}
} + CoroutineName("Mirai Bot")
)
.plus(CoroutineName("Mirai Bot"))
abstract val components: ConcurrentComponentStorage
init {
@Suppress("LeakingThis")
@ -87,86 +81,6 @@ internal abstract class AbstractBot constructor(
final override val asFriend: Friend by lazy { Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")) }
final override val asStranger: Stranger by lazy { Mirai.newStranger(bot, StrangerInfoImpl(bot.id, bot.nick)) }
///////////////////////////////////////////////////////////////////////////
// sync (// TODO: 2021/4/14 extract sync logic
///////////////////////////////////////////////////////////////////////////
val otherClientsLock = Mutex() // lock sync
// TODO: 2021/4/14 extract offlineListener
@OptIn(ExperimentalTime::class)
@Suppress("unused")
private val offlineListener: Listener<BotOfflineEvent> =
this@AbstractBot.eventChannel.parentJob(supervisor).subscribeAlways(
priority = MONITOR,
concurrency = ConcurrencyKind.LOCKED
) { event ->
val bot = bot.asQQAndroidBot()
if (
!event.bot.isActive // bot closed
// || _isConnecting // bot 还在登入 // TODO: 2021/4/14 处理还在登入?
) {
// Close network to avoid endless reconnection while network is ok
// https://github.com/mamoe/mirai/issues/894
kotlin.runCatching { network.close(null) }
return@subscribeAlways
}
/*
if (network.areYouOk() && event !is BotOfflineEvent.Force && event !is BotOfflineEvent.MsfOffline) {
// network 运行正常
return@subscribeAlways
}*/
when (event) {
is BotOfflineEvent.Active -> {
val cause = event.cause
val msg = if (cause == null) "" else " with exception: $cause"
bot.logger.info("Bot is closed manually $msg", cause)
network.close(null)
}
is BotOfflineEvent.Force -> {
bot.logger.info { "Connection occupied by another android device: ${event.message}" }
bot.asQQAndroidBot().accountSecretsFile.delete()
bot.client = bot.initClient()
if (event.reconnect) {
bot.logger.info { "Reconnecting..." }
// delay(3000)
} else {
network.close(null)
}
}
is BotOfflineEvent.MsfOffline,
is BotOfflineEvent.Dropped,
is BotOfflineEvent.RequireReconnect,
is BotOfflineEvent.PacketFactoryErrorCode
-> {
// nothing to do
}
}
if (event.reconnect) {
if (!network.isOk()) {
// normally closed
return@subscribeAlways
}
val causeMessage = event.castOrNull<BotOfflineEvent.CauseAware>()?.cause?.toString() ?: event.toString()
bot.logger.info { "Connection lost, retrying login ($causeMessage)" }
bot.launch {
val success: Boolean
val time = measureTime {
success = TODO("relogin")
}
if (success) {
logger.info { "Reconnected successfully in ${time.toHumanReadableString()}" }
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// network
///////////////////////////////////////////////////////////////////////////
@ -191,7 +105,6 @@ internal abstract class AbstractBot constructor(
kotlin.runCatching {
network.close(throwable)
}
offlineListener.cancel(CancellationException("Bot cancelled", throwable))
// help GC release instances
groups.forEach {

View File

@ -121,7 +121,7 @@ internal open class QQAndroidBot constructor(
private val networkLogger: MiraiLogger by lazy { configuration.networkLoggerSupplier(this) }
internal val components: ConcurrentComponentStorage by lazy {
override val components: ConcurrentComponentStorage by lazy {
ConcurrentComponentStorage().apply {
val components = this // avoid mistakes
set(SsoProcessorContext, SsoProcessorContextImpl(bot))

View File

@ -53,6 +53,8 @@ import net.mamoe.mirai.utils.verbose
* Uses [ContactCacheService].
*/
internal interface ContactUpdater {
val otherClientsLock: Mutex
/**
* Load all caches to the bot this [ContactUpdater] works for.
*
@ -75,6 +77,7 @@ internal class ContactUpdaterImpl(
val components: ComponentStorage,
private val logger: MiraiLogger,
) : ContactUpdater {
override val otherClientsLock: Mutex = Mutex()
private val cacheService get() = components[ContactCacheService]
private val lock = Mutex()

View File

@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.contact.appId
import net.mamoe.mirai.internal.contact.createOtherClient
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.ContactUpdater
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
@ -36,7 +37,7 @@ internal object MessageSvcRequestPushStatus : IncomingPacketFactory<Packet?>(
) {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
val packet = readUniPacket(RequestPushStatus.serializer())
bot.otherClientsLock.withLock {
bot.components[ContactUpdater].otherClientsLock.withLock {
val instanceInfo = packet.vecInstanceList?.firstOrNull()
val appId = instanceInfo?.iAppId ?: 1
return when (packet.status.toInt()) {

View File

@ -29,6 +29,7 @@ import net.mamoe.mirai.internal.contact.createOtherClient
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.components.ContactCacheService
import net.mamoe.mirai.internal.network.components.ContactUpdater
import net.mamoe.mirai.internal.network.protocol.data.jce.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769
import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline
@ -232,11 +233,6 @@ internal class StatSvc {
}
private fun String.ipToLong(): Long {
return split('.').foldIndexed(0L) { index: Int, acc: Long, s: String ->
acc or (((s.toLongOrNull() ?: 0) shl (index * 16)))
}
}
}
internal object ReqMSFOffline :
@ -283,7 +279,7 @@ internal class StatSvc {
IncomingPacketFactory<Packet?>("StatSvc.SvcReqMSFLoginNotify", "StatSvc.SvcReqMSFLoginNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? =
bot.otherClientsLock.withLock {
bot.components[ContactUpdater].otherClientsLock.withLock {
val notify = readUniPacket(SvcReqMSFLoginNotifyData.serializer())
val appId = notify.iAppId.toInt()