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

View File

@ -20,7 +20,7 @@ const profileLogger = createLogger('Profile')
let profileConfig: IProfileConfig let profileConfig: IProfileConfig
let profileConfigWriteQueue: Promise<void> = Promise.resolve() 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> { export async function getProfileConfig(force = false): Promise<IProfileConfig> {
if (force || !profileConfig) { if (force || !profileConfig) {
@ -63,37 +63,27 @@ export async function getProfileItem(id: string | undefined): Promise<IProfileIt
} }
export async function changeCurrentProfile(id: string): Promise<void> { export async function changeCurrentProfile(id: string): Promise<void> {
// 使用队列确保 profile 切换串行执行,避免竞态条件
changeProfileQueue = changeProfileQueue.then(async () => {
const { current } = await getProfileConfig() const { current } = await getProfileConfig()
if (current === id) return
if (current === id && targetProfileId !== id) {
return
}
targetProfileId = id
try { try {
await updateProfileConfig((config) => { await updateProfileConfig((config) => {
config.current = id config.current = id
return config return config
}) })
if (targetProfileId !== id) {
return
}
await restartCore() await restartCore()
if (targetProfileId === id) {
targetProfileId = null
}
} catch (e) { } catch (e) {
if (targetProfileId === id) { // 回滚配置
await updateProfileConfig((config) => { await updateProfileConfig((config) => {
config.current = current config.current = current
return config return config
}) })
targetProfileId = null
throw e throw e
} }
} })
await changeProfileQueue
} }
export async function updateProfileItem(item: IProfileItem): Promise<void> { 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 { quitWithoutCore, stopCore } from './core/manager'
import { triggerSysProxy } from './sys/sysproxy' import { triggerSysProxy } from './sys/sysproxy'
import { hideDockIcon, showDockIcon } from './resolve/tray' 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 export let mainWindow: BrowserWindow | null = null
let quitTimeout: NodeJS.Timeout | null = null let quitTimeout: NodeJS.Timeout | null = null
@ -78,7 +78,8 @@ function setupWindowEvents(
scheduleQuitWithoutCore(autoQuitWithoutCoreDelay) scheduleQuitWithoutCore(autoQuitWithoutCoreDelay)
} }
if (!silentStart) { // 开发模式下始终显示窗口
if (!silentStart || is.dev) {
clearQuitTimeout() clearQuitTimeout()
window.show() window.show()
window.focusOnWebView() window.focusOnWebView()