fix: resolve race conditions in config writes and profile switching

This commit is contained in:
xmk23333 2026-01-01 11:51:44 +08:00
parent 8384953fb7
commit 2c639d5bff
3 changed files with 44 additions and 49 deletions

View File

@ -11,6 +11,7 @@ import { createLogger } from '../utils/logger'
const controledMihomoLogger = createLogger('ControledMihomo')
let controledMihomoConfig: Partial<IMihomoConfig> // mihomo.yaml
let controledMihomoWriteQueue: Promise<void> = Promise.resolve()
export async function getControledMihomoConfig(force = false): Promise<Partial<IMihomoConfig>> {
if (force || !controledMihomoConfig) {
@ -39,6 +40,7 @@ export async function getControledMihomoConfig(force = false): Promise<Partial<I
}
export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
controledMihomoWriteQueue = controledMihomoWriteQueue.then(async () => {
const { controlDns = true, controlSniff = true } = await getAppConfig()
if (patch.hosts) {
@ -64,4 +66,6 @@ export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>):
await generateProfile()
await writeFile(controledMihomoConfigPath(), stringify(controledMihomoConfig), 'utf-8')
})
await controledMihomoWriteQueue
}

View File

@ -20,7 +20,7 @@ const profileLogger = createLogger('Profile')
let profileConfig: IProfileConfig
let profileConfigWriteQueue: Promise<void> = Promise.resolve()
let targetProfileId: string | null = null
let changeProfileQueue: Promise<void> = Promise.resolve()
export async function getProfileConfig(force = false): Promise<IProfileConfig> {
if (force || !profileConfig) {
@ -63,37 +63,27 @@ export async function getProfileItem(id: string | undefined): Promise<IProfileIt
}
export async function changeCurrentProfile(id: string): Promise<void> {
// 使用队列确保 profile 切换串行执行,避免竞态条件
changeProfileQueue = changeProfileQueue.then(async () => {
const { current } = await getProfileConfig()
if (current === id && targetProfileId !== id) {
return
}
targetProfileId = id
if (current === id) return
try {
await updateProfileConfig((config) => {
config.current = id
return config
})
if (targetProfileId !== id) {
return
}
await restartCore()
if (targetProfileId === id) {
targetProfileId = null
}
} catch (e) {
if (targetProfileId === id) {
// 回滚配置
await updateProfileConfig((config) => {
config.current = current
return config
})
targetProfileId = null
throw e
}
}
})
await changeProfileQueue
}
export async function updateProfileItem(item: IProfileItem): Promise<void> {

View File

@ -6,7 +6,7 @@ import { getAppConfig } from './config'
import { quitWithoutCore, stopCore } from './core/manager'
import { triggerSysProxy } from './sys/sysproxy'
import { hideDockIcon, showDockIcon } from './resolve/tray'
import icon from '../resources/icon.png?asset'
import icon from '../../resources/icon.png?asset'
export let mainWindow: BrowserWindow | null = null
let quitTimeout: NodeJS.Timeout | null = null
@ -78,7 +78,8 @@ function setupWindowEvents(
scheduleQuitWithoutCore(autoQuitWithoutCoreDelay)
}
if (!silentStart) {
// 开发模式下始终显示窗口
if (!silentStart || is.dev) {
clearQuitTimeout()
window.show()
window.focusOnWebView()