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( public fun CoroutineContext.childScope(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
): CoroutineScope { ): CoroutineScope {
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 val ctx = this + coroutineContext
return CoroutineScope(ctx + SupervisorJob(ctx.job)) 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 package net.mamoe.mirai.internal
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.ConcurrencyKind
import net.mamoe.mirai.event.EventChannel import net.mamoe.mirai.event.EventChannel
import net.mamoe.mirai.event.EventPriority.MONITOR
import net.mamoe.mirai.event.GlobalEventChannel 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.BotEvent
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.uin 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.internal.network.handler.NetworkHandler
import net.mamoe.mirai.supervisorJob import net.mamoe.mirai.supervisorJob
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
/**
* Protocol-irrelevant implementations
*/
internal abstract class AbstractBot constructor( internal abstract class AbstractBot constructor(
final override val configuration: BotConfiguration, final override val configuration: BotConfiguration,
final override val id: Long, final override val id: Long,
@ -48,20 +45,17 @@ internal abstract class AbstractBot constructor(
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// FASTEST INIT // FASTEST INIT
private val supervisor = SupervisorJob(configuration.parentCoroutineContext[Job])
final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) } final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) }
final override val coroutineContext: CoroutineContext = // for id final override val coroutineContext: CoroutineContext = configuration.parentCoroutineContext.childScopeContext(
configuration.parentCoroutineContext configuration.parentCoroutineContext.getOrElse(CoroutineExceptionHandler) {
.plus(supervisor) CoroutineExceptionHandler { _, e ->
.plus( logger.error("An exception was thrown under a coroutine of Bot", e)
configuration.parentCoroutineContext[CoroutineExceptionHandler] }
?: CoroutineExceptionHandler { _, e -> } + CoroutineName("Mirai Bot")
logger.error("An exception was thrown under a coroutine of Bot", e) )
}
) abstract val components: ConcurrentComponentStorage
.plus(CoroutineName("Mirai Bot"))
init { init {
@Suppress("LeakingThis") @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 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)) } 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 // network
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -191,7 +105,6 @@ internal abstract class AbstractBot constructor(
kotlin.runCatching { kotlin.runCatching {
network.close(throwable) network.close(throwable)
} }
offlineListener.cancel(CancellationException("Bot cancelled", throwable))
// help GC release instances // help GC release instances
groups.forEach { groups.forEach {

View File

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

View File

@ -53,6 +53,8 @@ import net.mamoe.mirai.utils.verbose
* Uses [ContactCacheService]. * Uses [ContactCacheService].
*/ */
internal interface ContactUpdater { internal interface ContactUpdater {
val otherClientsLock: Mutex
/** /**
* Load all caches to the bot this [ContactUpdater] works for. * Load all caches to the bot this [ContactUpdater] works for.
* *
@ -75,6 +77,7 @@ internal class ContactUpdaterImpl(
val components: ComponentStorage, val components: ComponentStorage,
private val logger: MiraiLogger, private val logger: MiraiLogger,
) : ContactUpdater { ) : ContactUpdater {
override val otherClientsLock: Mutex = Mutex()
private val cacheService get() = components[ContactCacheService] private val cacheService get() = components[ContactCacheService]
private val lock = Mutex() 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.contact.createOtherClient
import net.mamoe.mirai.internal.message.contextualBugReportException import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.Packet 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.handler.logger
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory 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? { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
val packet = readUniPacket(RequestPushStatus.serializer()) val packet = readUniPacket(RequestPushStatus.serializer())
bot.otherClientsLock.withLock { bot.components[ContactUpdater].otherClientsLock.withLock {
val instanceInfo = packet.vecInstanceList?.firstOrNull() val instanceInfo = packet.vecInstanceList?.firstOrNull()
val appId = instanceInfo?.iAppId ?: 1 val appId = instanceInfo?.iAppId ?: 1
return when (packet.status.toInt()) { 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.message.contextualBugReportException
import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.components.ContactCacheService 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.jce.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769 import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769
import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline 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 : internal object ReqMSFOffline :
@ -283,7 +279,7 @@ internal class StatSvc {
IncomingPacketFactory<Packet?>("StatSvc.SvcReqMSFLoginNotify", "StatSvc.SvcReqMSFLoginNotify") { IncomingPacketFactory<Packet?>("StatSvc.SvcReqMSFLoginNotify", "StatSvc.SvcReqMSFLoginNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? = override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? =
bot.otherClientsLock.withLock { bot.components[ContactUpdater].otherClientsLock.withLock {
val notify = readUniPacket(SvcReqMSFLoginNotifyData.serializer()) val notify = readUniPacket(SvcReqMSFLoginNotifyData.serializer())
val appId = notify.iAppId.toInt() val appId = notify.iAppId.toInt()