Skip to content

Commit 06c9d7e

Browse files
committed
pythongh-95348: Split clear_weakref into two functions
1 parent 398ed84 commit 06c9d7e

File tree

1 file changed

+48
-22
lines changed

1 file changed

+48
-22
lines changed

Objects/weakrefobject.c

+48-22
Original file line numberDiff line numberDiff line change
@@ -46,38 +46,54 @@ new_weakref(PyObject *ob, PyObject *callback)
4646
}
4747

4848

49-
/* This function clears the passed-in reference and removes it from the
50-
* list of weak references for the referent. This is the only code that
51-
* removes an item from the doubly-linked list of weak references for an
52-
* object; it is also responsible for clearing the callback slot.
49+
/* This function is responsible for clearing the callback slot. When using this
50+
* It must be called before calling remove_weakref, otherwise a segmentation fault will
51+
* occur at build time.
5352
*/
5453
static void
5554
clear_weakref(PyWeakReference *self)
5655
{
5756
PyObject *callback = self->wr_callback;
58-
59-
if (self->wr_object != Py_None) {
60-
PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
61-
62-
if (*list == self)
63-
/* If 'self' is the end of the list (and thus self->wr_next == NULL)
64-
then the weakref list itself (and thus the value of *list) will
65-
end up being set to NULL. */
66-
*list = self->wr_next;
67-
self->wr_object = Py_None;
68-
if (self->wr_prev != NULL)
69-
self->wr_prev->wr_next = self->wr_next;
70-
if (self->wr_next != NULL)
71-
self->wr_next->wr_prev = self->wr_prev;
72-
self->wr_prev = NULL;
73-
self->wr_next = NULL;
74-
}
7557
if (callback != NULL) {
7658
Py_DECREF(callback);
7759
self->wr_callback = NULL;
7860
}
7961
}
8062

63+
/* This function removes the pass in reference from the list of weak references
64+
* for the referent. This should be called from remove_weakref_fromt_referent
65+
* as it will pass the required list argument.
66+
*/
67+
static void
68+
remove_weakref(PyWeakReference *self, PyWeakReference **list)
69+
{
70+
if (*list == self)
71+
/* If 'self' is the end of the list (and thus self->wr_next == NULL)
72+
then the weakref list itself (and thus the value of *list) will
73+
end up being set to NULL. */
74+
*list = self->wr_next;
75+
self->wr_object = Py_None;
76+
if (self->wr_prev != NULL)
77+
self->wr_prev->wr_next = self->wr_next;
78+
if (self->wr_next != NULL)
79+
self->wr_next->wr_prev = self->wr_prev;
80+
self->wr_prev = NULL;
81+
self->wr_next = NULL;
82+
}
83+
84+
/* This function removes the pass in reference from the list of weak references
85+
* for the referent. This should be called after clear_weakref, otherwise a
86+
* segmentation fault will occur at build time.
87+
*/
88+
static void
89+
remove_weakref_from_referent(PyWeakReference *self)
90+
{
91+
if (self->wr_object != Py_None) {
92+
PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
93+
remove_weakref(self, list);
94+
}
95+
}
96+
8197
/* Cyclic gc uses this to *just* clear the passed-in reference, leaving
8298
* the callback intact and uncalled. It must be possible to call self's
8399
* tp_dealloc() after calling this, so self has to be left in a sane enough
@@ -100,6 +116,7 @@ _PyWeakref_ClearRef(PyWeakReference *self)
100116
callback = self->wr_callback;
101117
self->wr_callback = NULL;
102118
clear_weakref(self);
119+
remove_weakref_from_referent(self);
103120
self->wr_callback = callback;
104121
}
105122

@@ -108,6 +125,7 @@ weakref_dealloc(PyObject *self)
108125
{
109126
PyObject_GC_UnTrack(self);
110127
clear_weakref((PyWeakReference *) self);
128+
remove_weakref_from_referent((PyWeakReference *) self);
111129
Py_TYPE(self)->tp_free(self);
112130
}
113131

@@ -124,6 +142,7 @@ static int
124142
gc_clear(PyWeakReference *self)
125143
{
126144
clear_weakref(self);
145+
remove_weakref_from_referent(self);
127146
return 0;
128147
}
129148

@@ -562,6 +581,7 @@ proxy_dealloc(PyWeakReference *self)
562581
if (self->wr_callback != NULL)
563582
PyObject_GC_UnTrack((PyObject *)self);
564583
clear_weakref(self);
584+
remove_weakref_from_referent(self);
565585
PyObject_GC_Del(self);
566586
}
567587

@@ -958,8 +978,11 @@ PyObject_ClearWeakRefs(PyObject *object)
958978
/* Remove the callback-less basic and proxy references */
959979
if (*list != NULL && (*list)->wr_callback == NULL) {
960980
clear_weakref(*list);
961-
if (*list != NULL && (*list)->wr_callback == NULL)
981+
remove_weakref_from_referent(*list);
982+
if (*list != NULL && (*list)->wr_callback == NULL) {
962983
clear_weakref(*list);
984+
remove_weakref_from_referent(*list);
985+
}
963986
}
964987
if (*list != NULL) {
965988
PyWeakReference *current = *list;
@@ -972,6 +995,7 @@ PyObject_ClearWeakRefs(PyObject *object)
972995

973996
current->wr_callback = NULL;
974997
clear_weakref(current);
998+
remove_weakref_from_referent(current);
975999
if (callback != NULL) {
9761000
if (Py_REFCNT((PyObject *)current) > 0) {
9771001
handle_callback(current, callback);
@@ -1002,6 +1026,7 @@ PyObject_ClearWeakRefs(PyObject *object)
10021026
}
10031027
current->wr_callback = NULL;
10041028
clear_weakref(current);
1029+
remove_weakref_from_referent(current);
10051030
current = next;
10061031
}
10071032
for (i = 0; i < count; ++i) {
@@ -1036,5 +1061,6 @@ _PyStaticType_ClearWeakRefs(PyTypeObject *type)
10361061
weaklist before clearing its wr_object and wr_callback.
10371062
That is how we're able to loop over the list. */
10381063
clear_weakref((PyWeakReference *)*list);
1064+
remove_weakref_from_referent((PyWeakReference *)*list);
10391065
}
10401066
}

0 commit comments

Comments
 (0)