1
1
#include "Python.h"
2
+ #include "pycore_lock.h"
2
3
#include "pycore_modsupport.h" // _PyArg_NoKwnames()
3
4
#include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR()
4
5
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
5
6
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
6
7
8
+ #ifdef Py_GIL_DISABLED
9
+ /*
10
+ * Thread-safety for free-threaded builds
11
+ * ======================================
12
+ *
13
+ * In free-threaded builds we need to protect mutable state of:
14
+ *
15
+ * - The weakref
16
+ * - The referenced object
17
+ * - The linked-list of weakrefs
18
+ *
19
+ * The above may be modified concurrently when creating weakrefs, destroying
20
+ * weakrefs, or destroying the referenced object. Critical sections are
21
+ * used to protect the mutable state:
22
+ *
23
+ * - The weakref is protected by its per-object lock.
24
+ * - The referenced object is protected by its per-object lock.
25
+ * - The linked-list of weakrefs is protected by the referenced object's
26
+ * per-object lock.
27
+ * - Both locks must be held (using the two-variant form of critical sections)
28
+ * if both the weakref and referenced object or linked-list need to be
29
+ * modified.
30
+ *
31
+ */
32
+
33
+ typedef enum {
34
+ WR_UNLINK_NOT_STARTED ,
35
+ WR_UNLINK_STARTED ,
36
+ WR_UNLINK_DONE ,
37
+ } _PyWeakRefUnlinkState ;
38
+
39
+ /*
40
+ * _PyWeakRefUnlinker is used to coordinate concurrent attempts at unlinking
41
+ * weakrefs.
42
+ *
43
+ * XXX - More docs??
44
+ * XXX - Can this be a OnceFlag?
45
+ */
46
+ typedef struct _PyWeakRefUnlinker {
47
+ /* Holds a value from _PyWeakRefUnlinkState */
48
+ int state ;
49
+ PyEvent done ;
50
+ Py_ssize_t refcount ;
51
+ } _PyWeakRefUnlinker ;
52
+
53
+ static _PyWeakRefUnlinker *
54
+ _PyWeakRefUnlinker_New (void )
55
+ {
56
+ _PyWeakRefUnlinker * self = (_PyWeakRefUnlinker * )PyMem_RawCalloc (1 , sizeof (_PyWeakRefUnlinker ));
57
+ if (self == NULL ) {
58
+ PyErr_NoMemory ();
59
+ return NULL ;
60
+ }
61
+ self -> state = WR_UNLINK_NOT_STARTED ;
62
+ self -> done = (PyEvent ){0 };
63
+ self -> refcount = 1 ;
64
+ return self ;
65
+ }
66
+
67
+ static void
68
+ _PyWeakRefUnlinker_Incref (_PyWeakRefUnlinker * self )
69
+ {
70
+ _Py_atomic_add_ssize (& self -> refcount , 1 );
71
+ }
72
+
73
+ static void
74
+ _PyWeakRefUnlinker_Decref (_PyWeakRefUnlinker * self )
75
+ {
76
+ if (_Py_atomic_add_ssize (& self -> refcount , -1 ) == 1 ) {
77
+ PyMem_RawFree (self );
78
+ }
79
+ }
80
+
81
+ static int
82
+ _PyWeakRefUnlinker_Start (_PyWeakRefUnlinker * self )
83
+ {
84
+ int expected = WR_UNLINK_NOT_STARTED ;
85
+ return _Py_atomic_compare_exchange_int (& self -> state , & expected , WR_UNLINK_STARTED );
86
+ }
87
+
88
+ static void
89
+ _PyWeakRefUnlinker_Wait (_PyWeakRefUnlinker * self )
90
+ {
91
+ PyEvent_Wait (& self -> done );
92
+ }
93
+
94
+ static void
95
+ _PyWeakRefUnlinker_Finish (_PyWeakRefUnlinker * self )
96
+ {
97
+ _Py_atomic_exchange_int (& self -> state , WR_UNLINK_DONE );
98
+ _PyEvent_Notify (& self -> done );
99
+ }
100
+ #endif // Py_GIL_DISABLED
7
101
8
102
9
103
#define GET_WEAKREFS_LISTPTR (o ) \
@@ -24,7 +118,7 @@ _PyWeakref_GetWeakrefCount(PyWeakReference *head)
24
118
25
119
static PyObject * weakref_vectorcall (PyObject * self , PyObject * const * args , size_t nargsf , PyObject * kwnames );
26
120
27
- static void
121
+ static int
28
122
init_weakref (PyWeakReference * self , PyObject * ob , PyObject * callback )
29
123
{
30
124
self -> hash = -1 ;
@@ -34,9 +128,15 @@ init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
34
128
self -> wr_callback = Py_XNewRef (callback );
35
129
self -> vectorcall = weakref_vectorcall ;
36
130
#ifdef Py_GIL_DISABLED
131
+ self -> unlinker = _PyWeakRefUnlinker_New ();
132
+ if (self -> unlinker == NULL ) {
133
+ Py_XDECREF (self -> wr_callback );
134
+ return -1 ;
135
+ }
37
136
_PyObject_SetMaybeWeakref (ob );
38
137
_PyObject_SetMaybeWeakref ((PyObject * )self );
39
138
#endif
139
+ return 0 ;
40
140
}
41
141
42
142
static PyWeakReference *
@@ -46,12 +146,86 @@ new_weakref(PyObject *ob, PyObject *callback)
F438
46
146
47
147
result = PyObject_GC_New (PyWeakReference , & _PyWeakref_RefType );
48
148
if (result ) {
49
- init_weakref (result , ob , callback );
149
+ if (init_weakref (result , ob , callback ) < 0 ) {
150
+ return NULL ;
151
+ }
50
152
PyObject_GC_Track (result );
51
153
}
52
154
return result ;
53
155
}
54
156
157
+ #ifdef Py_GIL_DISABLED
158
+
159
+ static void
160
+ unlink_weakref_lock_held (PyWeakReference * self )
161
+ {
162
+ if (self -> wr_object != Py_None ) {
163
+ PyWeakReference * * list = GET_WEAKREFS_LISTPTR (self -> wr_object );
164
+ if (* list == self )
165
+ /* If 'self' is the end of the list (and thus self->wr_next == NULL)
166
+ then the weakref list itself (and thus the value of *list) will
167
+ end up being set to NULL. */
168
+ * list = self -> wr_next ;
169
+ self -> wr_object = Py_None ;
170
+ if (self -> wr_prev != NULL )
171
+ self -> wr_prev -> wr_next = self -> wr_next ;
172
+ if (self -> wr_next != NULL )
173
+ self -> wr_next -> wr_prev = self -> wr_prev ;
174
+ self -> wr_prev = NULL ;
175
+ self -> wr_next = NULL ;
176
+ }
177
+ }
178
+
179
+ static void
180
+ gc_unlink_weakref (PyWeakReference * self )
181
+ {
182
+ unlink_weakref_lock_held (self );
183
+ _PyWeakRefUnlinker_Finish (self -> unlinker );
184
+ }
185
+
186
+ static void
187
+ unlink_weakref (PyWeakReference * self )
188
+ {
189
+ Py_BEGIN_CRITICAL_SECTION2 (self -> wr_object , self );
190
+ unlink_weakref_lock_held (self );
191
+ Py_END_CRITICAL_SECTION2 ();
192
+ }
193
+
194
+ static void
195
+ clear_weakref (PyWeakReference * self )
196
+ {
197
+ _PyWeakRefUnlinker * unlinker = self -> unlinker ;
198
+ _PyWeakRefUnlinker_Incref (unlinker );
199
+
200
+ if (_PyWeakRefUnlinker_Start (unlinker )) {
201
+ unlink_weakref (self );
202
+ Py_BEGIN_CRITICAL_SECTION (self );
203
+ if (self -> wr_callback != NULL ) {
204
+ PyObject * callback = self -> wr_callback ;
205
+ self -> wr_callback = NULL ;
206
+ Py_DECREF (callback );
207
+ }
208
+ Py_END_CRITICAL_SECTION ();
209
+ _PyWeakRefUnlinker_Finish (unlinker );
210
+ }
211
+ else {
212
+ _PyWeakRefUnlinker_Wait (unlinker );
213
+ }
214
+
215
+ _PyWeakRefUnlinker_Decref (unlinker );
216
+ }
217
+
218
+ static void
219
+ gc_clear_weakref (PyWeakReference * self )
220
+ {
221
+ gc_unlink_weakref (self );
222
+ if (self -> wr_callback != NULL ) {
223
+ Py_DECREF (self -> wr_callback );
224
+ self -> wr_callback = NULL ;
225
+ }
226
+ }
227
+
228
+ #else
55
229
56
230
/* This function clears the passed-in reference and removes it from the
57
231
* list of weak references for the referent. This is the only code that
@@ -85,6 +259,8 @@ clear_weakref(PyWeakReference *self)
85
259
}
86
260
}
87
261
262
+ #endif // Py_GIL_DISABLED
263
+
88
264
/* Cyclic gc uses this to *just* clear the passed-in reference, leaving
89
265
* the callback intact and uncalled. It must be possible to call self's
90
266
* tp_dealloc() after calling this, so self has to be left in a sane enough
@@ -106,7 +282,11 @@ _PyWeakref_ClearRef(PyWeakReference *self)
106
282
/* Preserve and restore the callback around clear_weakref. */
107
283
callback = self -> wr_callback ;
108
284
self -> wr_callback = NULL ;
285
+ #ifdef Py_GIL_DISABLED
286
+ gc_unlink_weakref (self );
287
+ #else
109
288
clear_weakref (self );
289
+ #endif
110
290
self -> wr_callback = callback ;
111
291
}
112
292
@@ -115,6 +295,9 @@ weakref_dealloc(PyObject *self)
115
295
{
116
296
PyObject_GC_UnTrack (self );
117
297
clear_weakref ((PyWeakReference * ) self );
298
+ #ifdef Py_GIL_DISABLED
299
+ _PyWeakRefUnlinker_Decref (((PyWeakReference * )self )-> unlinker );
300
+ #endif
118
301
Py_TYPE (self )-> tp_free (self );
119
302
}
120
303
@@ -130,7 +313,11 @@ gc_traverse(PyWeakReference *self, visitproc visit, void *arg)
130
313
static int
131
314
gc_clear (PyWeakReference * self )
132
315
{
316
+ #ifdef Py_GIL_DISABLED
317
+ gc_clear_weakref (self );
318
+ #else
133
319
clear_weakref (self );
320
+ #endif
134
321
return 0 ;
135
322
}
136
323
@@ -324,7 +511,9 @@ new_weakref_lock_held(PyTypeObject *type, PyObject *ob, PyObject *callback)
324
511
them. */
325
512
PyWeakReference * self = (PyWeakReference * ) (type -> tp_alloc (type , 0 ));
326
513
if (self != NULL ) {
327
- init_weakref (self , ob , callback );
514
+ if (init_weakref (self , ob , callback ) < 0 ) {
515
+ return NULL ;
516
+ }
328
517
if (callback == NULL && type == & _PyWeakref_RefType ) {
329
518
insert_head (self , list );
330
519
}
@@ -976,6 +1165,7 @@ handle_callback(PyWeakReference *ref, PyObject *callback)
976
1165
Py_DECREF (cbresult );
977
1166
}
978
1167
1168
+
979
1169
/* This function is called by the tp_dealloc handler to clear weak references.
980
1170
*
981
1171
* This iterates through the weak references for 'object' and calls callbacks
@@ -995,6 +1185,57 @@ PyObject_ClearWeakRefs(PyObject *object)
995
1185
return ;
996
1186
}
997
1187
list = GET_WEAKREFS_LISTPTR (object );
1188
+ #ifdef Py_GIL_DISABLED
1189
+ /* Protect the linked-list and the head pointer in object */
1190
+ Py_BEGIN_CRITICAL_SECTION (object );
1191
+
1192
+ /* Remove the callback-less basic and proxy references, which always appear
1193
+ at the head of the list. There may be two of each - one live and one in
1194
+ the process of being destroyed.
1195
+ */
1196
+ for (;;) {
1197
+ if (* list != NULL && (* list )-> wr_callback == NULL ) {
1198
+ clear_weakref (* list );
1199
+ }
1200
+ else {
1201
+ break ;
1202
+ }
1203
+ }
1204
+
1205
+ /* Deal with non-canonical (subtypes or refs with callbacks) references.
1206
+ At this point no new weakrefs to this object can be created, so we
1207
+ should be safe to iterate until the list is empty.
1208
+ */
1209
+ PyObject * exc = PyErr_GetRaisedException ();
1210
+ while (* list != NULL ) {
1211
+ PyWeakReference * current = * list ;
1212
+ _PyWeakRefUnlinker * unlinker = current -> unlinker ;
1213
+ _PyWeakRefUnlinker_Incref (unlinker );
1214
+ if (_PyWeakRefUnlinker_Start (unlinker )) {
1215
+ PyObject * callback ;
1216
+ Py_BEGIN_CRITICAL_SECTION (current );
1217
+ callback = current -> wr_callback ;
1218
+ current -> wr_callback = NULL ;
1219
+ Py_END_CRITICAL_SECTION ();
1220
+ unlink_weakref (current );
1221
+ _PyWeakRefUnlinker_Finish (unlinker );
1222
+ if (callback != NULL ) {
1223
+ if (_Py_TryIncref ((PyObject * * ) & current , (PyObject * ) current )) {
1224
+ handle_callback (current , callback );
1225
+ Py_DECREF (current );
1226
+ }
1227
+ Py_DECREF (callback );
1228
+ }
1229
+ } else {
1230
+ _PyWeakRefUnlinker_Wait (unlinker );
1231
+ }
1232
+ _PyWeakRefUnlinker_Decref (unlinker );
1233
+ }
1234
+ assert (!PyErr_Occurred ());
1235
+ PyErr_SetRaisedException (exc );
1236
+
1237
+ Py_END_CRITICAL_SECTION ();
1238
+ #else
998
1239
/* Remove the callback-less basic and proxy references */
999
1240
if (* list != NULL && (* list )-> wr_callback == NULL ) {
1000
1241
clear_weakref (* list );
@@ -1056,6 +1297,7 @@ PyObject_ClearWeakRefs(PyObject *object)
1056
1297
assert (!PyErr_Occurred ());
1057
1298
PyErr_SetRaisedException (exc );
1058
1299
}
1300
+ #endif // Py_GIL_DISABLED
1059
1301
}
1060
1302
1061
1303
/* This function is called by _PyStaticType_Dealloc() to clear weak references.
0 commit comments