├── .gitignore ├── LICENSE ├── README.rst ├── docs ├── build.sh ├── conf.py └── index.rst ├── gevent_utils.py ├── setup.py └── tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | /docs/html 3 | /build 4 | /dist 5 | /MANIFEST 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Travis Cline All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. Redistributions in binary 8 | form must reproduce the above copyright notice, this list of conditions and 9 | the following disclaimer in the documentation and/or other materials 10 | provided with the distribution. Neither the name of the gevent-utils nor 11 | the names of its contributors may be used to endorse or promote products 12 | derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | gevent-utils 2 | ============ 3 | 4 | Collection of utilities useful for developing and debugging programs written 5 | with gevent. 6 | 7 | License 8 | ------- 9 | See LICENSE (New BSD) -------------------------------------------------------------------------------- /docs/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | PYTHONPATH=.. sphinx-build -b html . ./html 3 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | try: 3 | import sphinxtogithub 4 | optional_extensions = ['sphinxtogithub'] 5 | except ImportError: 6 | optional_extensions = [] 7 | 8 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary'] + optional_extensions 9 | master_doc = 'index' 10 | project = u'gevent-utils' 11 | copyright = u'2011, Travis Cline' 12 | version = '0.0.2' 13 | release = '0.0.2' 14 | 15 | exclude_patterns = [] 16 | add_module_names = True 17 | pygments_style = 'sphinx' 18 | 19 | html_show_sourcelink = False 20 | html_show_sphinx = False 21 | htmlhelp_basename = 'gevent-utilsdoc' 22 | latex_documents = [ 23 | ('index', 'gevent-utils.tex', u'gevent-utils Documentation', 24 | u'Travis Cline', 'manual'), 25 | ] 26 | man_pages = [ 27 | ('index', 'gevent-utils', u'gevent-utils Documentation', 28 | [u'Travis Cline'], 1) 29 | ] 30 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to gevent-utils's documentation! 2 | ======================================== 3 | 4 | .. automodule:: gevent_utils 5 | 6 | .. currentmodule:: gevent_utils 7 | 8 | .. autosummary:: 9 | 10 | BlockingDetector 11 | 12 | 13 | .. autoclass:: BlockingDetector -------------------------------------------------------------------------------- /gevent_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | gevent-utils 3 | ============ 4 | 5 | Debugging utilities for gevent. 6 | """ 7 | import math 8 | import signal 9 | import gevent 10 | 11 | arm_alarm = None 12 | if hasattr(signal, 'setitimer'): 13 | def alarm_itimer(seconds): 14 | signal.setitimer(signal.ITIMER_REAL, seconds) 15 | arm_alarm = alarm_itimer 16 | else: 17 | try: 18 | import itimer 19 | arm_alarm = itimer.alarm 20 | except ImportError: 21 | def alarm_signal(seconds): 22 | signal.alarm(math.ceil(seconds)) 23 | arm_alarm = alarm_signal 24 | 25 | class AlarmInterrupt(BaseException): pass 26 | 27 | class BlockingDetector(object): 28 | """ 29 | Utility class to detect thread blocking. Intended for debugging only. 30 | 31 | ``timeout`` is the number of seconds to wait before considering the thread 32 | blocked. 33 | 34 | Operates by setting and attempt to clear an alarm signal. The alarm signal 35 | cannot be cleared then the thread is considered blocked and 36 | ``BlockingDetector.alarm_handler`` is invoked with the signal and current 37 | frame. An ``AlarmInterrupt`` exception will be raised if the signal 38 | actually gets raised. 39 | 40 | Invoke via: gevent.spawn(BlockingDetector()) 41 | """ 42 | def __init__(self, timeout=1): 43 | self.timeout = timeout 44 | 45 | def __call__(self): 46 | """ 47 | Loop for 95% of our detection time and attempt to clear the signal. 48 | """ 49 | while True: 50 | self.set_signal() 51 | gevent.sleep(self.timeout * 0.95) 52 | self.clear_signal() 53 | # sleep the rest of the time 54 | gevent.sleep(self.timeout * 0.05) 55 | 56 | def alarm_handler(self, signum, frame): 57 | raise AlarmInterrupt('Blocking detected') 58 | 59 | def set_signal(self): 60 | tmp = signal.signal(signal.SIGALRM, self.alarm_handler) 61 | if tmp != self.alarm_handler: 62 | self._old_signal_handler = tmp 63 | arm_alarm(self.timeout) 64 | 65 | def clear_signal(self): 66 | if (hasattr(self, "_old_signal_handler") and self._old_signal_handler): 67 | signal.signal(signal.SIGALRM, self._old_signal_handler) 68 | signal.alarm(0) 69 | 70 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | __version__ = (0, 0, 2) 4 | 5 | setup( 6 | name = 'gevent_utils', 7 | version = '.'.join([str(x) for x in __version__]), 8 | py_modules = ['gevent_utils'], 9 | author = 'Travis Cline', 10 | author_email = 'travis.cline@gmail.com', 11 | description = 'miscellaneous gevent utilities', 12 | url = 'http://traviscline.github.com/gevent-utils', 13 | ) 14 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import time 2 | import mock 3 | import gevent 4 | from unittest import TestCase 5 | 6 | from gevent_utils import BlockingDetector, AlarmInterrupt 7 | 8 | class TestBlockingDetector(TestCase): 9 | 10 | @mock.patch.object(BlockingDetector, 'alarm_handler') 11 | def test_triggered_when_blocking(self, mock_alarm_handler): 12 | detector_greenlet = gevent.spawn(BlockingDetector(2)) 13 | gevent.sleep() 14 | 15 | self.assertFalse(mock_alarm_handler.called) 16 | time.sleep(1) 17 | self.assertFalse(mock_alarm_handler.called) 18 | time.sleep(1) 19 | 20 | # after two seconds of sleep the alarm_handler should have been called 21 | self.assertTrue(mock_alarm_handler.called) 22 | detector_greenlet.kill() 23 | 24 | def test_actual_exception_raised(self): 25 | detector_greenlet = gevent.spawn(BlockingDetector()) 26 | gevent.sleep() 27 | 28 | self.assertRaises(AlarmInterrupt, time.sleep, 1) 29 | detector_greenlet.kill() 30 | 31 | @mock.patch.object(BlockingDetector, 'alarm_handler') 32 | def test_not_triggered_when_cooperating(self, mock_alarm_handler): 33 | detector_greenlet = gevent.spawn(BlockingDetector()) 34 | gevent.sleep() 35 | 36 | self.assertFalse(mock_alarm_handler.called) 37 | gevent.sleep(2) 38 | self.assertFalse(mock_alarm_handler.called) 39 | detector_greenlet.kill() 40 | --------------------------------------------------------------------------------