Skip to content

Commit aaa05d9

Browse files
committed
[Fix] Close thread
1 parent abe90d4 commit aaa05d9

1 file changed

Lines changed: 13 additions & 0 deletions

File tree

src/pyfileindex/watcher.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import atexit
12
import threading
23
from collections.abc import Generator
34
from typing import Optional
@@ -12,6 +13,16 @@ class FileSystemWatcher:
1213
watchfiles is an optional dependency of pyfileindex: importing this
1314
module never requires it, only start() does.
1415
16+
Callers that hold a watcher alive for the lifetime of the process (e.g.
17+
via a path-keyed singleton cache) may never trigger its __del__ during
18+
normal execution -- it would only run during interpreter shutdown, by
19+
which point CPython may already be tearing down the modules and C-API
20+
state that the background thread's native extension depends on, which
21+
can crash the process instead of cleanly joining the thread. start()
22+
therefore registers stop() with atexit, so every watcher is joined
23+
while the interpreter is still fully intact, before that teardown
24+
begins.
25+
1526
Args:
1627
path (str): file system path to watch
1728
"""
@@ -53,12 +64,14 @@ def start(self) -> None:
5364
next(self._generator)
5465
self._thread = threading.Thread(target=self._worker, daemon=True)
5566
self._thread.start()
67+
atexit.register(self.stop)
5668

5769
def stop(self) -> None:
5870
"""
5971
Stop the background file system watcher. Safe to call even if no
6072
watcher is running.
6173
"""
74+
atexit.unregister(self.stop)
6275
if self._stop_event is not None:
6376
self._stop_event.set()
6477
if self._thread is not None:

0 commit comments

Comments
 (0)