From ffa737be98994770aeab8e9628acf0562209fae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=20=ED=95=9C?= Date: Sun, 19 Jul 2026 22:05:33 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9E=AC=EC=8B=A4=ED=96=89=20=EC=8B=9C=20?= =?UTF-8?q?=EB=B0=B1=EC=A7=80+=EC=9D=91=EB=8B=B5=EC=97=86=EC=9D=8C(SMB=20?= =?UTF-8?q?=EC=86=8C=EC=8A=A4)=20=EC=88=98=EC=A0=95=20=E2=80=94=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EC=8B=9C=20=EC=9B=8C=EC=B2=98/=EC=8D=B8?= =?UTF-8?q?=EB=84=A4=EC=9D=BC=EB=B0=B1=ED=95=84=EC=9D=84=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=8A=A4=EB=A0=88=EB=93=9C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 증상: SMB(네트워크) 소스 + 자료 많음 상태에서 재실행하면 창이 백지 + "응답 없음". 원인: setup()가 메인 스레드에서 watcher.refresh()를 동기 호출 → 각 로컬 소스에 debouncer.watch(경로, Recursive)를 등록하는데, 재실행 직후 SMB 공유가 느리거나 재연결 중이면 watch()가 블로킹 → 메인 스레드(이벤트 루프)가 멈춰 창이 안 그려짐. (처음 열 땐 소스가 없어 no-op이라 정상이었고, 소스 추가는 워커 스레드에서 refresh해 안 멈췄음 — 재실행 때만 메인 스레드에서 SMB를 건드려 멈춤.) 수정: 워처 매니저는 즉시 등록하되 refresh(watch 등록)는 백그라운드 스레드로, 시작 썸네일 백필(enqueue_pending)도 백그라운드로 이동. setup()가 SMB를 동기로 건드리지 않으므로 창이 즉시 렌더되고 응답 유지. 검증(로컬): 소스 있는 상태 재실행 → 창 ~0.7초에 렌더(사이드바+그리드 30셀), 새 파일 추가 시 워처가 3초 내 감지해 재스캔(31셀). 회귀 없음. (실제 SMB 블로킹은 로컬 재현 불가 — 메인 스레드에서 SMB를 건드리는 유일한 지점을 제거.) Co-Authored-By: Claude Fable 5 --- src-tauri/src/main.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index cef5538..2abb3af 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -103,8 +103,19 @@ fn main() { let _ = emit_handle.emit("thumbs-ready", ids); }, )); - // 앱 시작 시 미처리 썸네일 백필 - thumbs.enqueue_pending(&db, None); + // 앱 시작 시 미처리 썸네일 백필 — 백그라운드로. + // (대용량/네트워크(SMB) 소스에서 시작 시 메인 스레드 블로킹 방지) + { + let thumbs = thumbs.clone(); + let db = db.clone(); + std::thread::Builder::new() + .name("thumb-backfill".into()) + .spawn(move || { + archive_indexer::prio::set_current_thread_low(); + thumbs.enqueue_pending(&db, None); + }) + .ok(); + } #[cfg(debug_assertions)] tracing::info!( @@ -131,13 +142,26 @@ fn main() { )), }); - // 파일 워처: 등록된 로컬 소스 전체 감시 (AppState manage 이후에 초기화) + // 파일 워처: 매니저는 즉시 등록하되, 실제 watch() 등록은 백그라운드에서. + // SMB/네트워크 경로에서 watch()가 느리거나 블로킹될 수 있어 시작(메인 스레드)을 막지 않게 한다. + // (재실행 시 백지+응답없음 원인: 소스가 있는 상태에서 setup의 refresh가 SMB watch()로 멈춤) { let state = app.state::(); let manager = watcher::WatcherManager::new(app.handle().clone()); - manager.refresh(&state.db); let _ = state.watchers.set(manager); } + { + let handle = app.handle().clone(); + std::thread::Builder::new() + .name("watch-refresh".into()) + .spawn(move || { + let state = handle.state::(); + if let Some(w) = state.watchers.get() { + w.refresh(&state.db); + } + }) + .ok(); + } // 원격 소스 자동 재연결 (키체인 자격증명 기준 — 볼트는 잠금해제 후 수동). {