mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #16602: When a weakref's target was part of a long deallocation chain, the object could remain reachable through its weakref even though its refcount had dropped to zero.
Thanks to Eugene Toder for diagnosing and reporting the issue.
This commit is contained in:
parent
bd5279ea24
commit
d38c990bb7
5 changed files with 39 additions and 4 deletions
|
@ -66,7 +66,17 @@ PyAPI_FUNC(Py_ssize_t) _PyWeakref_GetWeakrefCount(PyWeakReference *head);
|
||||||
|
|
||||||
PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self);
|
PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self);
|
||||||
|
|
||||||
#define PyWeakref_GET_OBJECT(ref) (((PyWeakReference *)(ref))->wr_object)
|
/* Explanation for the Py_REFCNT() check: when a weakref's target is part
|
||||||
|
of a long chain of deallocations which triggers the trashcan mechanism,
|
||||||
|
clearing the weakrefs can be delayed long after the target's refcount
|
||||||
|
has dropped to zero. In the meantime, code accessing the weakref will
|
||||||
|
be able to "see" the target object even though it is supposed to be
|
||||||
|
unreachable. See issue #16602. */
|
||||||
|
|
||||||
|
#define PyWeakref_GET_OBJECT(ref) \
|
||||||
|
(Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \
|
||||||
|
? ((PyWeakReference *)(ref))->wr_object \
|
||||||
|
: Py_None)
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -774,6 +774,27 @@ class ReferencesTestCase(TestBase):
|
||||||
self.assertEqual(hash(a), hash(42))
|
self.assertEqual(hash(a), hash(42))
|
||||||
self.assertRaises(TypeError, hash, b)
|
self.assertRaises(TypeError, hash, b)
|
||||||
|
|
||||||
|
def test_trashcan_16602(self):
|
||||||
|
# Issue #16602: when a weakref's target was part of a long
|
||||||
|
# deallocation chain, the trashcan mechanism could delay clearing
|
||||||
|
# of the weakref and make the target object visible from outside
|
||||||
|
# code even though its refcount had dropped to 0. A crash ensued.
|
||||||
|
class C(object):
|
||||||
|
def __init__(self, parent):
|
||||||
|
if not parent:
|
||||||
|
return
|
||||||
|
wself = weakref.ref(self)
|
||||||
|
def cb(wparent):
|
||||||
|
o = wself()
|
||||||
|
self.wparent = weakref.ref(parent, cb)
|
||||||
|
|
||||||
|
d = weakref.WeakKeyDictionary()
|
||||||
|
root = c = C(None)
|
||||||
|
for n in range(100):
|
||||||
|
d[c] = c = C(c)
|
||||||
|
del root
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
class SubclassableWeakrefTestCase(TestBase):
|
class SubclassableWeakrefTestCase(TestBase):
|
||||||
|
|
||||||
|
|
|
@ -990,6 +990,7 @@ Jim Tittsler
|
||||||
Frank J. Tobin
|
Frank J. Tobin
|
||||||
R Lindsay Todd
|
R Lindsay Todd
|
||||||
Bennett Todd
|
Bennett Todd
|
||||||
|
Eugene Toder
|
||||||
Matias Torchinsky
|
Matias Torchinsky
|
||||||
Sandro Tosi
|
Sandro Tosi
|
||||||
Richard Townsend
|
Richard Townsend
|
||||||
|
|
|
@ -9,6 +9,10 @@ What's New in Python 2.7.4
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #16602: When a weakref's target was part of a long deallocation
|
||||||
|
chain, the object could remain reachable through its weakref even though
|
||||||
|
its refcount had dropped to zero.
|
||||||
|
|
||||||
- Issue #9011: Fix hacky AST code that modified the CST when compiling
|
- Issue #9011: Fix hacky AST code that modified the CST when compiling
|
||||||
a negated numeric literal.
|
a negated numeric literal.
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,8 @@ clear_weakref(PyWeakReference *self)
|
||||||
{
|
{
|
||||||
PyObject *callback = self->wr_callback;
|
PyObject *callback = self->wr_callback;
|
||||||
|
|
||||||
if (PyWeakref_GET_OBJECT(self) != Py_None) {
|
if (self->wr_object != Py_None) {
|
||||||
PyWeakReference **list = GET_WEAKREFS_LISTPTR(
|
PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
|
||||||
PyWeakref_GET_OBJECT(self));
|
|
||||||
|
|
||||||
if (*list == self)
|
if (*list == self)
|
||||||
/* If 'self' is the end of the list (and thus self->wr_next == NULL)
|
/* If 'self' is the end of the list (and thus self->wr_next == NULL)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue