Skip to content

Commit 50cd1c0

Browse files
author
fdrake
committed
Allocating a new weakref object can cause existing weakref objects for
the same object to be collected by the cyclic GC support if they are only referenced by a cycle. If the weakref being collected was one of the weakrefs without callbacks, some local variables for the constructor became invalid and have to be re-computed. The test caused a segfault under a debug build without the fix applied. git-svn-id: http://svn.python.org/projects/python/trunk@35204 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 8b237f4 commit 50cd1c0

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

Lib/test/test_weakref.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import gc
12
import sys
23
import unittest
34
import UserList
@@ -591,6 +592,37 @@ def cb(self, ignore):
591592
gc.collect()
592593
self.assertEqual(alist, [])
593594

595+
def test_gc_during_ref_creation(self):
596+
self.check_gc_during_creation(weakref.ref)
597+
598+
def test_gc_during_proxy_creation(self):
599+
self.check_gc_during_creation(weakref.proxy)
600+
601+
def check_gc_during_creation(self, makeref):
602+
thresholds = gc.get_threshold()
603+
gc.set_threshold(1, 1, 1)
604+
gc.collect()
605+
class A:
606+
pass
607+
608+
def callback(*args):
609+
pass
610+
611+
referenced = A()
612+
613+
a = A()
614+
a.a = a
615+
a.wr = makeref(referenced)
616+
617+
try:
618+
# now make sure the object and the ref get labeled as
619+
# cyclic trash:
620+
a = A()
621+
a.wrc = weakref.ref(referenced, callback)
622+
623+
finally:
624+
gc.set_threshold(*thresholds)
625+
594626
class Object:
595627
def __init__(self, arg):
596628
self.arg = arg

Objects/weakrefobject.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -630,16 +630,23 @@ PyWeakref_NewRef(PyObject *ob, PyObject *callback)
630630
/* return existing weak reference if it exists */
631631
result = ref;
632632
if (result != NULL)
633-
Py_XINCREF(result);
633+
Py_INCREF(result);
634634
else {
635+
/* Note: new_weakref() can trigger cyclic GC, so the weakref
636+
list on ob can be mutated. This means that the ref and
637+
proxy pointers we got back earlier may have been collected,
638+
so we need to compute these values again before we use
639+
them. */
635640
result = new_weakref(ob, callback);
636641
if (result != NULL) {
637642
if (callback == NULL) {
638643
insert_head(result, list);
639644
}
640645
else {
641-
PyWeakReference *prev = (proxy == NULL) ? ref : proxy;
646+
PyWeakReference *prev;
642647

648+
get_basic_refs(*list, &ref, &proxy);
649+
prev = (proxy == NULL) ? ref : proxy;
643650
if (prev == NULL)
644651
insert_head(result, list);
645652
else
@@ -672,8 +679,13 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
672679
/* attempt to return an existing weak reference if it exists */
673680
result = proxy;
674681
if (result != NULL)
675-
Py_XINCREF(result);
682+
Py_INCREF(result);
676683
else {
684+
/* Note: new_weakref() can trigger cyclic GC, so the weakref
685+
list on ob can be mutated. This means that the ref and
686+
proxy pointers we got back earlier may have been collected,
687+
so we need to compute these values again before we use
688+
them. */
677689
result = new_weakref(ob, callback);
678690
if (result != NULL) {
679691
PyWeakReference *prev;
@@ -682,6 +694,7 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
682694
result->ob_type = &_PyWeakref_CallableProxyType;
683695
else
684696
result->ob_type = &_PyWeakref_ProxyType;
697+
get_basic_refs(*list, &ref, &proxy);
685698
if (callback == NULL)
686699
prev = ref;
687700
else

0 commit comments

Comments
 (0)