Skip to content

Commit

Permalink
Merge pull request #251 from chevah/250-inotify-close-block
Browse files Browse the repository at this point in the history
Fix inotify stop dead lock. Closes #250
  • Loading branch information
tamland committed Jul 17, 2014
2 parents be2bff9 + d03ea36 commit 3bbcd5b
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ Fork the `repository`_ on GitHub and send a pull request, or file an issue
ticket at the `issue tracker`_. For general help and questions use the official
`mailing list`_ or ask on `stackoverflow`_ with tag `python-watchdog`.

Create and activate your virtual environment, then::

pip install pytest
pip install -e .
py.tests tests


Supported Platforms
-------------------
* Linux 2.6 (inotify)
Expand Down
5 changes: 4 additions & 1 deletion src/watchdog/observers/inotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@

import os
import threading
from .inotify_buffer import InotifyBuffer
from .inotify_buffer import InotifyBuffer, STOP_EVENT

from watchdog.observers.api import (
EventEmitter,
Expand Down Expand Up @@ -130,6 +130,9 @@ def queue_events(self, timeout):
with self._lock:
event = self._inotify.read_event()

if event is STOP_EVENT:
return

if isinstance(event, tuple):
move_from, move_to = event
src_path = self._decode_path(move_from.src_path)
Expand Down
5 changes: 5 additions & 0 deletions src/watchdog/observers/inotify_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from watchdog.utils import DaemonThread
from .inotify_c import Inotify

STOP_EVENT = object()


class _Worker(DaemonThread):
"""
Expand Down Expand Up @@ -94,6 +96,9 @@ def close(self):
self._worker.stop()
self._inotify.close()
self._worker.join()
# Add the stop event to unblock the read_event which waits for
# events in the queue... even after inotify buffer is closed.
self._put(STOP_EVENT)

def _put(self, elem):
self._lock.acquire()
Expand Down
22 changes: 22 additions & 0 deletions tests/test_inotify_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import pytest
from tests import tmpdir, p # pytest magic
from .shell import mkdir, touch, mv
from watchdog.observers.api import ObservedWatch
from watchdog.utils import platform

pytestmark = pytest.mark.skipif(not platform.is_linux(), reason="")
if platform.is_linux():
from watchdog.observers.inotify import InotifyEmitter
from watchdog.observers.inotify_buffer import InotifyBuffer


Expand Down Expand Up @@ -100,3 +102,23 @@ def test_move_internal_batch(p):
assert frm.name == to.name
i += 1
inotify.close()


def test_close_clean(tmpdir):
"""
On InotifyBuffer.close() InotifyBuffer.read_event() is un-blocked so that
Inotify thread waiting for it can be closed.
This is also a test for Inotify.queue_events handling of STOP_EVENT and
InotifyBuffer.close() is test as side effect of Inotify.stop()
"""
watch = ObservedWatch(path=tmpdir, recursive=False)
emitter = InotifyEmitter([], watch)
inotify_buffer = emitter._inotify
# Remove delay to speed up test.
inotify_buffer.delay = 0
emitter.start()

emitter.stop()
emitter.join(1)
assert not emitter.isAlive()

0 comments on commit 3bbcd5b

Please sign in to comment.