tornado and tr/http_test.py: fix HTTPServer-related circular references.
Among other things, every web.RequestHandler object would end up leaking and
need to be picked up later by the garbage collector.
Add some checks to tr/http_test to make sure that these particular bugs
don't come back. Note that we *didn't* add a GcChecker to
http_test.HttpTest itself (the bulk of the tests) yet, because it still
leaks objects, albeit fewer than before. We can fix the rest in a future
commit.
Change-Id: Icd66b5fe1b775b80e6fc0f199cb2b89d60a57ee6
diff --git a/tornado/httpclient.py b/tornado/httpclient.py
index 6ad591b..849b762 100644
--- a/tornado/httpclient.py
+++ b/tornado/httpclient.py
@@ -180,6 +180,7 @@
if self._instance_cache.get(self.io_loop) is not self:
raise RuntimeError("inconsistent AsyncHTTPClient cache")
del self._instance_cache[self.io_loop]
+ self._instance_cache = None
def fetch(self, request, callback, **kwargs):
"""Executes a request, calling callback with an `HTTPResponse`.
@@ -419,7 +420,7 @@
def __init__(self, code, message=None, response=None):
self.code = code
message = message or httplib.responses.get(code, "Unknown")
- self.response = response
+ # self.response = response # removed to avoid circular references
Exception.__init__(self, "HTTP %d: %s" % (self.code, message))
diff --git a/tornado/httpserver.py b/tornado/httpserver.py
index 0c5bd00..bbe4a21 100644
--- a/tornado/httpserver.py
+++ b/tornado/httpserver.py
@@ -182,6 +182,9 @@
callback()
# Delete any unfinished callbacks to break up reference cycles.
self._write_callback = None
+ self._header_callback = None
+ self.request_callback = None
+ self.stream = None
def write(self, chunk, callback=None):
"""Writes a chunk of output to the stream."""
diff --git a/tornado/testing.py b/tornado/testing.py
index de67214..9e29292 100644
--- a/tornado/testing.py
+++ b/tornado/testing.py
@@ -165,6 +165,9 @@
self.io_loop.stop()
self.__running = False
self.__stopped = True
+ if self.__timeout is not None:
+ self.io_loop.remove_timeout(self.__timeout)
+ self.__timeout = None
def wait(self, condition=None, timeout=5):
"""Runs the IOLoop until stop is called or timeout has passed.
diff --git a/tornado/web.py b/tornado/web.py
index 10e3420..5a0422f 100644
--- a/tornado/web.py
+++ b/tornado/web.py
@@ -689,7 +689,8 @@
content_length = sum(len(part) for part in self._write_buffer)
self.set_header("Content-Length", content_length)
- if hasattr(self.request, "connection"):
+ # NOTE(apenwarr): breaks httpserver.HTTPConnection.set_close_callback
+ if 0 and hasattr(self.request, "connection"):
# Now that the request is finished, clear the callback we
# set on the IOStream (which would otherwise prevent the
# garbage collection of the RequestHandler when there
@@ -702,6 +703,7 @@
self._log()
self._finished = True
self.on_finish()
+ self.ui = None
def send_error(self, status_code=500, **kwargs):
"""Sends the given HTTP error code to the browser.