Skip to content

Commit db698a5

Browse files
fix: reading inotify file descriptor after closing it. (#1081)
1 parent 73d5612 commit db698a5

File tree

2 files changed

+22
-13
lines changed

2 files changed

+22
-13
lines changed

src/watchdog/observers/inotify_c.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import struct
1010
import threading
1111
from ctypes import c_char_p, c_int, c_uint32
12-
from functools import partial, reduce
13-
from typing import TYPE_CHECKING, Any, Callable
12+
from functools import reduce
13+
from typing import TYPE_CHECKING
1414

1515
from watchdog.utils import UnsupportedLibcError
1616

@@ -150,16 +150,26 @@ def __init__(self, path: bytes, *, recursive: bool = False, event_mask: int | No
150150
self._inotify_fd = inotify_fd
151151
self._lock = threading.Lock()
152152
self._closed = False
153-
self._waiting_to_read = True
153+
self._is_reading = True
154154
self._kill_r, self._kill_w = os.pipe()
155155

156+
# _check_inotify_fd will return true if we can read _inotify_fd without blocking
156157
if hasattr(select, "poll"):
157158
self._poller = select.poll()
158159
self._poller.register(self._inotify_fd, select.POLLIN)
159160
self._poller.register(self._kill_r, select.POLLIN)
160-
self._poll: Callable[[], Any] = partial(self._poller.poll)
161+
162+
def do_poll() -> bool:
163+
return any(fd == self._inotify_fd for fd, _ in self._poller.poll())
164+
165+
self._check_inotify_fd = do_poll
161166
else:
162-
self._poll = partial(select.select, (self._inotify_fd, self._kill_r))
167+
168+
def do_select() -> bool:
169+
result = select.select([self._inotify_fd, self._kill_r], [], [])
170+
return self._inotify_fd in result[0]
171+
172+
self._check_inotify_fd = do_select
163173

164174
# Stores the watch descriptor for a given path.
165175
self._wd_for_path: dict[bytes, int] = {}
@@ -249,7 +259,7 @@ def close(self) -> None:
249259
wd = self._wd_for_path[self._path]
250260
inotify_rm_watch(self._inotify_fd, wd)
251261

252-
if self._waiting_to_read:
262+
if self._is_reading:
253263
# inotify_rm_watch() should write data to _inotify_fd and wake
254264
# the thread, but writing to the kill channel will gaurentee this
255265
os.write(self._kill_w, b"!")
@@ -291,25 +301,24 @@ def _recursive_simulate(src_path: bytes) -> list[InotifyEvent]:
291301
events.append(e)
292302
return events
293303

294-
event_buffer = None
304+
event_buffer = b""
295305
while True:
296306
try:
297307
with self._lock:
298308
if self._closed:
299309
return []
300310

301-
self._waiting_to_read = True
311+
self._is_reading = True
302312

303-
self._poll()
313+
if self._check_inotify_fd():
314+
event_buffer = os.read(self._inotify_fd, event_buffer_size)
304315

305316
with self._lock:
306-
self._waiting_to_read = False
317+
self._is_reading = False
307318

308319
if self._closed:
309320
self._close_resources()
310321
return []
311-
312-
event_buffer = os.read(self._inotify_fd, event_buffer_size)
313322
except OSError as e:
314323
if e.errno == errno.EINTR:
315324
continue

tests/test_inotify_c.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def register(self, fd, *args, **kwargs):
8181

8282
def poll(self, *args, **kwargs):
8383
if self._fake:
84-
return None
84+
return [(inotify_fd, select.POLLIN)]
8585
return self._orig.poll(*args, **kwargs)
8686

8787
os_read_bkp = os.read

0 commit comments

Comments
 (0)