Commit 4c9c260c authored by Philip Jenvey's avatar Philip Jenvey

#6990: clear threading.local's key only after its thread state is removed:

fixes local subclasses leaving old state around after a ref cycle GC which
could be recycled by new locals
(backported from r75123)
parent b4dc921e
import gc
import threading
import unittest import unittest
from doctest import DocTestSuite from doctest import DocTestSuite
from test import test_support from test import test_support
...@@ -8,7 +10,6 @@ class ThreadingLocalTest(unittest.TestCase): ...@@ -8,7 +10,6 @@ class ThreadingLocalTest(unittest.TestCase):
# of a threading.local derived class, the per-thread dictionary # of a threading.local derived class, the per-thread dictionary
# is created but not correctly set on the object. # is created but not correctly set on the object.
# The first member set may be bogus. # The first member set may be bogus.
import threading
import time import time
class Local(threading.local): class Local(threading.local):
def __init__(self): def __init__(self):
...@@ -29,6 +30,44 @@ class ThreadingLocalTest(unittest.TestCase): ...@@ -29,6 +30,44 @@ class ThreadingLocalTest(unittest.TestCase):
for t in threads: for t in threads:
t.join() t.join()
def test_derived_cycle_dealloc(self):
# http://bugs.python.org/issue6990
class Local(threading.local):
pass
locals = None
passed = [False]
e1 = threading.Event()
e2 = threading.Event()
def f():
# 1) Involve Local in a cycle
cycle = [Local()]
cycle.append(cycle)
cycle[0].foo = 'bar'
# 2) GC the cycle (triggers threadmodule.c::local_clear
# before local_dealloc)
del cycle
gc.collect()
e1.set()
e2.wait()
# 4) New Locals should be empty
passed[0] = all(not hasattr(local, 'foo') for local in locals)
t = threading.Thread(target=f)
t.start()
e1.wait()
# 3) New Locals should recycle the original's address. Creating
# them in the thread overwrites the thread state and avoids the
# bug
locals = [Local() for i in range(10)]
e2.set()
t.join()
self.assertTrue(passed[0])
def test_main(): def test_main():
suite = DocTestSuite('_threading_local') suite = DocTestSuite('_threading_local')
......
...@@ -9,6 +9,12 @@ What's New in Python 2.5.5? ...@@ -9,6 +9,12 @@ What's New in Python 2.5.5?
*Release date: XX-XXX-20XX* *Release date: XX-XXX-20XX*
Core and builtins
-----------------
- Issue #6990: Fix threading.local subclasses leaving old state around
after a reference cycle GC which could be recycled by new locals.
Library Library
------- -------
......
...@@ -228,7 +228,6 @@ local_traverse(localobject *self, visitproc visit, void *arg) ...@@ -228,7 +228,6 @@ local_traverse(localobject *self, visitproc visit, void *arg)
static int static int
local_clear(localobject *self) local_clear(localobject *self)
{ {
Py_CLEAR(self->key);
Py_CLEAR(self->args); Py_CLEAR(self->args);
Py_CLEAR(self->kw); Py_CLEAR(self->kw);
Py_CLEAR(self->dict); Py_CLEAR(self->dict);
...@@ -250,6 +249,7 @@ local_dealloc(localobject *self) ...@@ -250,6 +249,7 @@ local_dealloc(localobject *self)
PyDict_DelItem(tstate->dict, self->key); PyDict_DelItem(tstate->dict, self->key);
} }
Py_XDECREF(self->key);
local_clear(self); local_clear(self);
self->ob_type->tp_free((PyObject*)self); self->ob_type->tp_free((PyObject*)self);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment