fix(watchdog): move observer refresh outside registry lock in add/remove/clear

Dedent _refresh_observer() calls out of with self._lock: blocks in _WatchRegistry.add(), remove(), and clear() to break an AB-BA deadlock between _WatchRegistry._lock (L1) and BaseObserver._lock (L2) in `/opt/venv-a0/lib/python3.12/site-packages/watchdog/observers/api.py`. The main thread held L1 while calling unschedule_all() which needs L2; the observer dispatch thread held L2 while calling dispatch() which needs L1.
This commit is contained in:
linkliti 2026-05-11 21:54:46 +03:00
parent 11765adb75
commit f5379ba282

View file

@ -130,8 +130,8 @@ class _WatchRegistry:
pending.timer.cancel()
self._watches.update(watches)
self._watch_ids_by_group[id] = set(watches)
if not self._batching:
self._refresh_observer()
if not self._batching:
self._refresh_observer()
def remove(self, id: str) -> bool:
with self._lock:
@ -142,9 +142,9 @@ class _WatchRegistry:
pending = self._pending_batches.pop(watch_id, None)
if pending and pending.timer:
pending.timer.cancel()
if removed and not self._batching:
self._refresh_observer()
return removed
if removed and not self._batching:
self._refresh_observer()
return removed
def clear(self) -> None:
with self._lock:
@ -152,8 +152,8 @@ class _WatchRegistry:
self._watch_ids_by_group.clear()
pending_batches = list(self._pending_batches.values())
self._pending_batches.clear()
if not self._batching:
self._refresh_observer()
if not self._batching:
self._refresh_observer()
for pending in pending_batches:
if pending.timer:
pending.timer.cancel()