mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
channel: master version: 1.0.0 source-ref: master published-at-utc: 2026-03-13T15:19:28Z
152 lines
3.3 KiB
Go
152 lines
3.3 KiB
Go
package backend
|
|
|
|
import (
|
|
"ant-chrome/backend/internal/logger"
|
|
"fmt"
|
|
"os/exec"
|
|
stdruntime "runtime"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type browserProcessSnapshot struct {
|
|
profileID string
|
|
cmd *exec.Cmd
|
|
}
|
|
|
|
func (a *App) stopRuntimeServices() {
|
|
a.stopServicesOnce.Do(func() {
|
|
log := logger.New("App")
|
|
|
|
a.stopAllBrowserProcessesForExit(log)
|
|
|
|
if a.xrayMgr != nil {
|
|
a.xrayMgr.StopAll()
|
|
}
|
|
a.clearProfileXrayBridges()
|
|
|
|
if a.singboxMgr != nil {
|
|
a.singboxMgr.StopAll()
|
|
}
|
|
|
|
if a.clashMgr != nil {
|
|
a.clashMgr.StopAll()
|
|
}
|
|
|
|
if a.speedScheduler != nil {
|
|
a.speedScheduler.Stop()
|
|
a.speedScheduler = nil
|
|
}
|
|
|
|
if a.launchServer != nil {
|
|
if err := a.launchServer.Stop(); err != nil {
|
|
log.Error("LaunchServer 关闭失败", logger.F("error", err))
|
|
}
|
|
a.launchServer = nil
|
|
}
|
|
|
|
if err := killResidualRuntimeProcesses(a.appRoot); err != nil {
|
|
log.Error("退出前清理残留进程失败", logger.F("error", err.Error()))
|
|
}
|
|
})
|
|
}
|
|
|
|
func (a *App) finalizeShutdown() {
|
|
a.finalizeOnce.Do(func() {
|
|
if a.db != nil {
|
|
a.db.Close()
|
|
a.db = nil
|
|
}
|
|
if err := logger.Close(); err != nil {
|
|
fmt.Printf("关闭日志系统失败: %v\n", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func (a *App) stopAllBrowserProcessesForExit(log *logger.Logger) {
|
|
if a.browserMgr == nil {
|
|
return
|
|
}
|
|
|
|
stoppedAt := time.Now().Format(time.RFC3339)
|
|
|
|
a.browserMgr.Mutex.Lock()
|
|
processes := make([]browserProcessSnapshot, 0, len(a.browserMgr.BrowserProcesses))
|
|
for profileID, cmd := range a.browserMgr.BrowserProcesses {
|
|
if profile, ok := a.browserMgr.Profiles[profileID]; ok && profile != nil {
|
|
profile.Running = false
|
|
profile.LastStopAt = stoppedAt
|
|
}
|
|
if cmd != nil && cmd.Process != nil {
|
|
processes = append(processes, browserProcessSnapshot{
|
|
profileID: profileID,
|
|
cmd: cmd,
|
|
})
|
|
}
|
|
}
|
|
a.browserMgr.BrowserProcesses = make(map[string]*exec.Cmd)
|
|
a.browserMgr.Mutex.Unlock()
|
|
|
|
if len(processes) == 0 {
|
|
return
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
for _, item := range processes {
|
|
wg.Add(1)
|
|
go func(item browserProcessSnapshot) {
|
|
defer wg.Done()
|
|
|
|
pid := 0
|
|
if item.cmd != nil && item.cmd.Process != nil {
|
|
pid = item.cmd.Process.Pid
|
|
}
|
|
log.Info("退出前关闭浏览器实例", logger.F("profile_id", item.profileID), logger.F("pid", pid))
|
|
if err := stopProcessCmdForShutdown(item.cmd); err != nil {
|
|
log.Error("退出前关闭浏览器实例失败", logger.F("profile_id", item.profileID), logger.F("pid", pid), logger.F("error", err.Error()))
|
|
}
|
|
}(item)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func stopProcessCmdForShutdown(cmd *exec.Cmd) error {
|
|
if cmd == nil || cmd.Process == nil {
|
|
return nil
|
|
}
|
|
|
|
pid := cmd.Process.Pid
|
|
if pid > 0 {
|
|
if err := forceKillProcessTree(pid); err == nil || isProcessAlreadyFinished(err) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
err := cmd.Process.Kill()
|
|
if err == nil || isProcessAlreadyFinished(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func forceKillProcessTree(pid int) error {
|
|
if pid <= 0 {
|
|
return nil
|
|
}
|
|
if stdruntime.GOOS != "windows" {
|
|
return fmt.Errorf("force kill process tree unsupported on %s", stdruntime.GOOS)
|
|
}
|
|
|
|
killCmd := exec.Command("taskkill", "/F", "/T", "/PID", fmt.Sprintf("%d", pid))
|
|
hideWindow(killCmd)
|
|
err := killCmd.Run()
|
|
if err == nil {
|
|
_ = waitProcessExitWindows(pid, 1500*time.Millisecond)
|
|
return nil
|
|
}
|
|
if waitProcessExitWindows(pid, 300*time.Millisecond) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|