[inotify] Avoid bad file descriptor shutdown (#895)

* avoid bad file descriptor shutdown

* add to changelog
diff --git a/changelog.rst b/changelog.rst
index 7549fec..977cad8 100644
--- a/changelog.rst
+++ b/changelog.rst
@@ -11,7 +11,8 @@
 - [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
 ~~~~~
diff --git a/src/watchdog/observers/inotify_c.py b/src/watchdog/observers/inotify_c.py
index 6161f4f..d942d2a 100644
--- a/src/watchdog/observers/inotify_c.py
+++ b/src/watchdog/observers/inotify_c.py
@@ -286,6 +286,8 @@
             except OSError as e:
                 if e.errno == errno.EINTR:
                     continue
+                elif e.errno == errno.EBADF:
+                    return []
                 else:
                     raise
             break
diff --git a/tests/test_inotify_buffer.py b/tests/test_inotify_buffer.py
index da4a210..40e85cb 100644
--- a/tests/test_inotify_buffer.py
+++ b/tests/test_inotify_buffer.py
@@ -22,6 +22,7 @@
 
 import os
 import random
+import time
 
 from watchdog.observers.inotify_buffer import InotifyBuffer
 
@@ -132,8 +133,28 @@
     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()