Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid bad file descriptor shutdown #895

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion changelog.rst
Expand Up @@ -11,7 +11,8 @@ Unreleased
- [fsevents] Fix flakey test to assert that there are no errors when stopping the emitter.
- [watchmedo] Make ``auto-restart`` restart the sub-process if it terminates. (`#896 <https://github.com/gorakhargosh/watchdog/pull/896>`__)
- [watchmedo] Avoid zombie sub-processes when running ``shell-command`` without ``--wait``. (`#405 <https://github.com/gorakhargosh/watchdog/issues/405>`__)
- Thanks to our beloved contributors: @samschott, @taleinat
- [inotify] Suppress occasional ``OSError: [Errno 9] Bad file descriptor`` at shutdown (`#805 <https://github.com/gorakhargosh/watchdog/issues/805>`__)
- Thanks to our beloved contributors: @samschott, @taleinat, @altendky

2.1.8
~~~~~
Expand Down
2 changes: 2 additions & 0 deletions src/watchdog/observers/inotify_c.py
Expand Up @@ -286,6 +286,8 @@ def _recursive_simulate(src_path):
except OSError as e:
if e.errno == errno.EINTR:
continue
elif e.errno == errno.EBADF:
return []
else:
raise
break
Expand Down
25 changes: 23 additions & 2 deletions tests/test_inotify_buffer.py
Expand Up @@ -22,6 +22,7 @@

import os
import random
import time

from watchdog.observers.inotify_buffer import InotifyBuffer

Expand Down Expand Up @@ -132,8 +133,28 @@ def test_unmount_watched_directory_filesystem(p):
assert not inotify.is_alive()


def test_close_should_terminate_thread(p):
inotify = InotifyBuffer(p('').encode(), recursive=True)
def delay_call(function, seconds):
def delayed(*args, **kwargs):
time.sleep(seconds)

return function(*args, **kwargs)

return delayed


class InotifyBufferDelayedRead(InotifyBuffer):
def run(self, *args, **kwargs):
# Introduce a delay to trigger the race condition where the file descriptor is
# closed prior to a read being triggered.
self._inotify.read_events = delay_call(function=self._inotify.read_events, seconds=1)

return super().run(*args, **kwargs)


@pytest.mark.parametrize(argnames="cls", argvalues=[InotifyBuffer, InotifyBufferDelayedRead])
def test_close_should_terminate_thread(p, cls):
inotify = cls(p('').encode(), recursive=True)

assert inotify.is_alive()
inotify.close()
assert not inotify.is_alive()