[tor-commits] [stem/master] Fixing testing thread leak and adding check for it
atagar at torproject.org
atagar at torproject.org
Mon Apr 23 02:31:28 UTC 2012
commit 7271f04a9cdaf6bb6eb633424891c17654b2807f
Author: Damian Johnson <atagar at torproject.org>
Date: Sun Apr 22 18:38:16 2012 -0700
Fixing testing thread leak and adding check for it
Python does not gracefully handle lingering threads at shutdown, even if
they're daemons (... which is dumb since that's the only point of the daemon
thread). Having threads causes occasional nonsensical stacktraces with things
like "'NoneType' object has no attribute 'socket'" for module references.
Adding a check after running our integ tests that only the main thread remains,
and erroring if that's not the case. This also fixes the thread leak that was
probably the issue in...
https://trac.torproject.org/projects/tor/ticket/5512
---
run_tests.py | 24 +++++++++++++++++++++---
stem/control.py | 9 ++++++++-
test/integ/control/base_controller.py | 8 ++++----
3 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/run_tests.py b/run_tests.py
index dcfcdef..9ee6042 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -9,6 +9,7 @@ import sys
import time
import getopt
import unittest
+import threading
import StringIO
import test.output
@@ -204,6 +205,9 @@ def load_user_configuration(test_config):
if __name__ == '__main__':
start_time = time.time()
+ # override flag to indicate at the end that testing failed somewhere
+ testing_failed = False
+
# loads and validates our various configurations
test_config = stem.util.conf.get_config("test")
@@ -320,6 +324,20 @@ if __name__ == '__main__':
print
test.output.print_logging(logging_buffer)
+
+ # We should have joined on all threads. If not then that indicates a
+ # leak that could both likely be a bug and disrupt further targets.
+
+ active_threads = threading.enumerate()
+
+ if len(active_threads) > 1:
+ test.output.print_line("Threads lingering after test run:", *ERROR_ATTR)
+
+ for lingering_thread in active_threads:
+ test.output.print_line(" %s" % lingering_thread, *ERROR_ATTR)
+
+ testing_failed = True
+ break
except KeyboardInterrupt:
test.output.print_line(" aborted starting tor: keyboard interrupt\n", *ERROR_ATTR)
break
@@ -357,11 +375,11 @@ if __name__ == '__main__':
if runtime < 1: runtime_label = "(%0.1f seconds)" % runtime
else: runtime_label = "(%i seconds)" % runtime
- if error_tracker.has_error_occured():
- test.output.print_line("TESTING FAILED %s" % runtime_label, term.Color.RED, term.Attr.BOLD)
+ if testing_failed or error_tracker.has_error_occured():
+ test.output.print_line("TESTING FAILED %s" % runtime_label, *ERROR_ATTR)
for line in error_tracker:
- test.output.print_line(" %s" % line, term.Color.RED, term.Attr.BOLD)
+ test.output.print_line(" %s" % line, *ERROR_ATTR)
else:
test.output.print_line("TESTING PASSED %s" % runtime_label, term.Color.GREEN, term.Attr.BOLD)
print
diff --git a/stem/control.py b/stem/control.py
index 55c6e7a..0ed4032 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -15,7 +15,8 @@ BaseController - Base controller class asynchronous message handling.
|- close - shuts down our connection to the tor process
|- get_socket - provides the socket used for control communication
|- add_status_listener - notifies a callback of changes in our status
- +- remove_status_listener - prevents further notification of status changes
+ |- remove_status_listener - prevents further notification of status changes
+ +- __enter__ / __exit__ - manages socket connection
"""
import time
@@ -269,6 +270,12 @@ class BaseController:
self._status_listeners = new_listeners
return is_changed
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exit_type, value, traceback):
+ self.close()
+
def _handle_event(self, event_message):
"""
Callback to be overwritten by subclasses for event listening. This is
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index 1abab93..940ef99 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -40,8 +40,8 @@ class TestBaseController(unittest.TestCase):
"""
if test.runner.Torrc.PORT in test.runner.get_runner().get_options():
- controller = stem.control.BaseController.from_port(control_port = test.runner.CONTROL_PORT)
- self.assertTrue(isinstance(controller, stem.control.BaseController))
+ with stem.control.BaseController.from_port(control_port = test.runner.CONTROL_PORT) as controller:
+ self.assertTrue(isinstance(controller, stem.control.BaseController))
else:
self.assertRaises(stem.socket.SocketError, stem.control.BaseController.from_port, "127.0.0.1", test.runner.CONTROL_PORT)
@@ -51,8 +51,8 @@ class TestBaseController(unittest.TestCase):
"""
if test.runner.Torrc.SOCKET in test.runner.get_runner().get_options():
- controller = stem.control.BaseController.from_socket_file(test.runner.CONTROL_SOCKET_PATH)
- self.assertTrue(isinstance(controller, stem.control.BaseController))
+ with stem.control.BaseController.from_socket_file(socket_path = test.runner.CONTROL_SOCKET_PATH) as controller:
+ self.assertTrue(isinstance(controller, stem.control.BaseController))
else:
self.assertRaises(stem.socket.SocketError, stem.control.BaseController.from_socket_file, test.runner.CONTROL_SOCKET_PATH)
More information about the tor-commits
mailing list