diff --git a/eventlet/debug.py b/eventlet/debug.py index 70d0aca..50586b3 100644 --- a/eventlet/debug.py +++ b/eventlet/debug.py @@ -132,13 +132,14 @@ def tpool_exceptions(state = False): from eventlet import tpool tpool.QUIET = not state -def hub_blocking_detection(state = False): +def hub_blocking_detection(state = False, resolution = 1): """Toggles whether Eventlet makes an effort to detect blocking behavior in other code. It does this by setting a SIGALARM with a short timeout. """ from eventlet import hubs hubs.get_hub().debug_blocking = state + hubs.get_hub().debug_blocking_resolution = resolution if(not state): hubs.get_hub().block_detect_post() diff --git a/eventlet/hubs/hub.py b/eventlet/hubs/hub.py index 4aaf49a..0f9d0cb 100644 --- a/eventlet/hubs/hub.py +++ b/eventlet/hubs/hub.py @@ -4,6 +4,13 @@ import traceback import warnings import signal +HAS_ITIMER = False +try: + import itimer + HAS_ITIMER = True +except ImportError: + pass + from eventlet.support import greenlets as greenlet, clear_sys_exc_info from eventlet.hubs import timer from eventlet import patcher @@ -71,13 +78,19 @@ class BaseHub(object): self.timers_canceled = 0 self.debug_exceptions = True self.debug_blocking = False + self.debug_blocking_resolution = 1 def block_detect_pre(self): # shortest alarm we can possibly raise is one second tmp = signal.signal(signal.SIGALRM, alarm_handler) if tmp != alarm_handler: self._old_signal_handler = tmp - signal.alarm(1) + + if HAS_ITIMER: + itimer.alarm(self.debug_blocking_resolution) + else: + signal.alarm(self.debug_blocking_resolution) + def block_detect_post(self): if (hasattr(self, "_old_signal_handler") and self._old_signal_handler): diff --git a/tests/__init__.py b/tests/__init__.py index 5884f09..4845574 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -92,6 +92,16 @@ def skip_on_windows(func): import sys return skip_if(sys.platform.startswith('win'))(func) +def skip_if_no_itimer(func): + """ Decorator that skips a test if the `itimer` module isn't found """ + has_itimer = False + try: + import itimer + has_itimer = True + except ImportError: + pass + return skip_unless(has_itimer)(func) + class TestIsTakingTooLong(Exception): """ Custom exception class to be raised when a test's runtime exceeds a limit. """ diff --git a/tests/hub_test.py b/tests/hub_test.py index d3f9561..d7559db 100644 --- a/tests/hub_test.py +++ b/tests/hub_test.py @@ -1,4 +1,4 @@ -from tests import LimitedTestCase, main, skip_with_pyevent +from tests import LimitedTestCase, main, skip_with_pyevent, skip_if_no_itimer import time import eventlet from eventlet import hubs @@ -154,6 +154,18 @@ class TestHubBlockingDetector(LimitedTestCase): self.assertRaises(RuntimeError, gt.wait) debug.hub_blocking_detection(False) + @skip_if_no_itimer + def test_block_detect_with_itimer(self): + def look_im_blocking(): + import time + time.sleep(0.5) + + from eventlet import debug + debug.hub_blocking_detection(True, resolution=0.1) + gt = eventlet.spawn(look_im_blocking) + self.assertRaises(RuntimeError, gt.wait) + debug.hub_blocking_detection(False) + class TestSuspend(LimitedTestCase): TEST_TIMEOUT=3 def test_suspend_doesnt_crash(self):