]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add an option to log a stack trace any time the ioloop is blocked for
authorBen Darnell <bdarnell@beaker.local>
Wed, 21 Apr 2010 18:57:44 +0000 (11:57 -0700)
committerBen Darnell <bdarnell@beaker.local>
Wed, 21 Apr 2010 18:57:44 +0000 (11:57 -0700)
more than a specified amount of time.

tornado/ioloop.py

index 7cfd707a05d75049ba21f8bf9798b40f403aef08..f9bb1a255af798a58cfbfad26951ca4d22455508 100644 (file)
@@ -22,6 +22,12 @@ import os
 import logging
 import select
 import time
+import traceback
+
+try:
+    import signal
+except ImportError:
+    signal = None
 
 try:
     import fcntl
@@ -96,6 +102,7 @@ class IOLoop(object):
         self._timeouts = []
         self._running = False
         self._stopped = False
+        self._blocking_log_threshold = None
 
         # Create a pipe that we send bogus data to when we want to wake
         # the I/O loop when it is idle
@@ -154,6 +161,23 @@ class IOLoop(object):
         except (OSError, IOError):
             logging.debug("Error deleting fd from IOLoop", exc_info=True)
 
+    def set_blocking_log_threshold(self, s):
+        """Logs a stack trace if the ioloop is blocked for more than s seconds.
+        Pass None to disable.  Requires python 2.6 on a unixy platform.
+        """
+        if not hasattr(signal, "setitimer"):
+            logging.error("set_blocking_log_threshold requires a signal module "
+                       "with the setitimer method")
+            return
+        self._blocking_log_threshold = s
+        if s is not None:
+            signal.signal(signal.SIGALRM, self._handle_alarm)
+
+    def _handle_alarm(self, signal, frame):
+        logging.warning('IOLoop blocked for %f seconds in\n%s',
+                     self._blocking_log_threshold,
+                     ''.join(traceback.format_stack(frame)))
+
     def start(self):
         """Starts the I/O loop.
 
@@ -192,6 +216,11 @@ class IOLoop(object):
             if not self._running:
                 break
 
+            if self._blocking_log_threshold is not None:
+                # clear alarm so it doesn't fire while poll is waiting for
+                # events.
+                signal.setitimer(signal.ITIMER_REAL, 0, 0)
+
             try:
                 event_pairs = self._impl.poll(poll_timeout)
             except Exception, e:
@@ -201,6 +230,10 @@ class IOLoop(object):
                 else:
                     raise
 
+            if self._blocking_log_threshold is not None:
+                signal.setitimer(signal.ITIMER_REAL,
+                                 self._blocking_log_threshold, 0)
+
             # Pop one fd at a time from the set of pending fds and run
             # its handler. Since that handler may perform actions on
             # other file descriptors, there may be reentrant calls to
@@ -224,6 +257,8 @@ class IOLoop(object):
                                   fd, exc_info=True)
         # reset the stopped flag so another start/stop pair can be issued
         self._stopped = False
+        if self._blocking_log_threshold is not None:
+            signal.setitimer(signal.ITIMER_REAL, 0, 0)
 
     def stop(self):
         """Stop the loop after the current event loop iteration is complete.