Skip to content

Commit 1dd7fa8

Browse files
authored
[inotify] Avoid bad file descriptor shutdown (#895)
* avoid bad file descriptor shutdown * add to changelog
1 parent df1574c commit 1dd7fa8

File tree

3 files changed

+27
-3
lines changed

3 files changed

+27
-3
lines changed

changelog.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ Unreleased
1111
- [fsevents] Fix flakey test to assert that there are no errors when stopping the emitter.
1212
- [watchmedo] Make ``auto-restart`` restart the sub-process if it terminates. (`#896 <https://github.com/gorakhargosh/watchdog/pull/896>`__)
1313
- [watchmedo] Avoid zombie sub-processes when running ``shell-command`` without ``--wait``. (`#405 <https://github.com/gorakhargosh/watchdog/issues/405>`__)
14-
- Thanks to our beloved contributors: @samschott, @taleinat
14+
- [inotify] Suppress occasional ``OSError: [Errno 9] Bad file descriptor`` at shutdown (`#805 <https://github.com/gorakhargosh/watchdog/issues/805>`__)
15+
- Thanks to our beloved contributors: @samschott, @taleinat, @altendky
1516

1617
2.1.8
1718
~~~~~

src/watchdog/observers/inotify_c.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ def _recursive_simulate(src_path):
286286
except OSError as e:
287287
if e.errno == errno.EINTR:
288288
continue
289+
elif e.errno == errno.EBADF:
290+
return []
289291
else:
290292
raise
291293
break

tests/test_inotify_buffer.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import os
2424
import random
25+
import time
2526

2627
from watchdog.observers.inotify_buffer import InotifyBuffer
2728

@@ -132,8 +133,28 @@ def test_unmount_watched_directory_filesystem(p):
132133
assert not inotify.is_alive()
133134

134135

135-
def test_close_should_terminate_thread(p):
136-
inotify = InotifyBuffer(p('').encode(), recursive=True)
136+
def delay_call(function, seconds):
137+
def delayed(*args, **kwargs):
138+
time.sleep(seconds)
139+
140+
return function(*args, **kwargs)
141+
142+
return delayed
143+
144+
145+
class InotifyBufferDelayedRead(InotifyBuffer):
146+
def run(self, *args, **kwargs):
147+
# Introduce a delay to trigger the race condition where the file descriptor is
148+
# closed prior to a read being triggered.
149+
self._inotify.read_events = delay_call(function=self._inotify.read_events, seconds=1)
150+
151+
return super().run(*args, **kwargs)
152+
153+
154+
@pytest.mark.parametrize(argnames="cls", argvalues=[InotifyBuffer, InotifyBufferDelayedRead])
155+
def test_close_should_terminate_thread(p, cls):
156+
inotify = cls(p('').encode(), recursive=True)
157+
137158
assert inotify.is_alive()
138159
inotify.close()
139160
assert not inotify.is_alive()

0 commit comments

Comments
 (0)