From 4a993820049a6f83a7b34c0d345ffc6cc8bc6333 Mon Sep 17 00:00:00 2001 From: matevip Date: Mon, 6 Apr 2026 12:01:09 +0800 Subject: [PATCH] fix(browser): fix idle watchdog leak on repeated start/stop cycles --- .../vip/mate/tool/builtin/BrowserUseTool.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/tool/builtin/BrowserUseTool.java b/mateclaw-server/src/main/java/vip/mate/tool/builtin/BrowserUseTool.java index 2aa161fd..6b6beb0e 100644 --- a/mateclaw-server/src/main/java/vip/mate/tool/builtin/BrowserUseTool.java +++ b/mateclaw-server/src/main/java/vip/mate/tool/builtin/BrowserUseTool.java @@ -282,6 +282,12 @@ public class BrowserUseTool { return ok("No browser running"); } + // 取消空闲看门狗(避免 stop 后定时任务继续运行) + ScheduledFuture watchdog = session.idleWatchdog; + if (watchdog != null && !watchdog.isDone()) { + watchdog.cancel(false); + } + String cdpUrl = session.cdpUrl; boolean wasCdp = session.connectedViaCdp; session.close(); // Only closes Browser/Context, not the shared Playwright instance @@ -624,15 +630,26 @@ public class BrowserUseTool { } private void scheduleIdleCheck(String sessionKey) { - scheduler.scheduleAtFixedRate(() -> { - BrowserSession session = sessions.get(sessionKey); - if (session == null) return; - long idleMinutes = (System.currentTimeMillis() - session.lastActivity) / 60_000; + BrowserSession session = sessions.get(sessionKey); + if (session == null) return; + + // 取消已有的看门狗(防止 start→stop→start 导致多个定时任务累积) + ScheduledFuture existing = session.idleWatchdog; + if (existing != null && !existing.isDone()) { + existing.cancel(false); + } + + ScheduledFuture future = scheduler.scheduleAtFixedRate(() -> { + BrowserSession s = sessions.get(sessionKey); + if (s == null) return; + long idleMinutes = (System.currentTimeMillis() - s.lastActivity) / 60_000; if (idleMinutes >= IDLE_TIMEOUT_MINUTES) { log.info("[BrowserUse] Idle timeout ({}min), stopping session: {}", idleMinutes, sessionKey); doStop(sessionKey); } }, IDLE_TIMEOUT_MINUTES, 5, TimeUnit.MINUTES); + + session.idleWatchdog = future; } @PreDestroy @@ -691,6 +708,8 @@ public class BrowserUseTool { final boolean connectedViaCdp; final String cdpUrl; volatile long lastActivity; + /** 空闲看门狗定时任务(stop 时取消,避免泄漏) */ + volatile ScheduledFuture idleWatchdog; BrowserSession(Browser browser, BrowserContext context, Page page, boolean headed, boolean connectedViaCdp, String cdpUrl) {