diff --git a/backend/Cargo.lock b/backend/Cargo.lock index ec020d8..b1e86c3 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1558,7 +1558,7 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mangalord" -version = "0.90.8" +version = "0.90.9" dependencies = [ "anyhow", "argon2", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 8979566..a4b3cff 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mangalord" -version = "0.90.8" +version = "0.90.9" edition = "2021" default-run = "mangalord" diff --git a/backend/src/crawler/browser_manager.rs b/backend/src/crawler/browser_manager.rs index 47c5ac0..eda32ae 100644 --- a/backend/src/crawler/browser_manager.rs +++ b/backend/src/crawler/browser_manager.rs @@ -234,12 +234,20 @@ impl BrowserManager { await_drain(&self.active, drain_deadline).await; self.set_phase(RestartPhase::Restarting); - let relaunch = { + // Take the dead handle out under the lock, release the lock, THEN run + // the (slow) Chromium teardown — so a worker that raced past the drain + // can't block on `acquire()` behind one dead browser's close(). Mirrors + // the idle reaper's take-drop-close ordering. Re-acquire to relaunch. + let dead = { let mut guard = self.inner.lock().await; guard.shared = None; - if let Some(handle) = guard.handle.take() { - let _ = handle.close().await; - } + guard.handle.take() + }; + if let Some(handle) = dead { + let _ = handle.close().await; + } + let relaunch = { + let mut guard = self.inner.lock().await; self.launch_into(&mut guard).await }; @@ -257,9 +265,14 @@ impl BrowserManager { /// Used on daemon shutdown. After this returns the next acquire will /// re-launch from scratch. pub async fn shutdown(&self) { - let mut guard = self.inner.lock().await; - guard.shared = None; - if let Some(handle) = guard.handle.take() { + // Take-then-drop-then-close: don't hold the lock across Chromium + // teardown (see `invalidate` / the idle reaper). + let handle = { + let mut guard = self.inner.lock().await; + guard.shared = None; + guard.handle.take() + }; + if let Some(handle) = handle { let _ = handle.close().await; } } @@ -278,9 +291,16 @@ impl BrowserManager { /// Idempotent: calling on an already-invalidated manager is a /// no-op. pub async fn invalidate(&self) { - let mut guard = self.inner.lock().await; - guard.shared = None; - if let Some(handle) = guard.handle.take() { + // Take the handle out under the lock, then release the lock BEFORE the + // slow Chromium close() — otherwise every other worker's `acquire()` + // serializes behind one dead browser's teardown. Matches the idle + // reaper's ordering at the bottom of this file. + let handle = { + let mut guard = self.inner.lock().await; + guard.shared = None; + guard.handle.take() + }; + if let Some(handle) = handle { let _ = handle.close().await; tracing::warn!("BrowserManager: handle invalidated — next acquire will relaunch"); } diff --git a/frontend/package.json b/frontend/package.json index 00d1ce6..8c00b98 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "mangalord-frontend", - "version": "0.90.8", + "version": "0.90.9", "private": true, "type": "module", "scripts": {