1212#include "pycore_interp.h" // PyInterpreterState.atexit
1313#include "pycore_pystate.h" // _PyInterpreterState_GET
1414
15+ /* Note: anything declared as static in this file assumes the lock is held
16+ * (except for the Python-level functions) */
17+ #define _PyAtExit_LOCK (state ) PyMutex_Lock(&state->lock);
18+ #define _PyAtExit_UNLOCK (state ) PyMutex_Unlock(&state->lock);
19+
1520/* ===================================================================== */
1621/* Callback machinery. */
1722
@@ -38,13 +43,15 @@ PyUnstable_AtExit(PyInterpreterState *interp,
3843 callback -> next = NULL ;
3944
4045 struct atexit_state * state = & interp -> atexit ;
46+ _PyAtExit_LOCK (state );
4147 if (state -> ll_callbacks == NULL ) {
4248 state -> ll_callbacks = callback ;
4349 state -> last_ll_callback = callback ;
4450 }
4551 else {
4652 state -> last_ll_callback -> next = callback ;
4753 }
54+ _PyAtExit_UNLOCK (state );
4855 return 0 ;
4956}
5057
99106_PyAtExit_Fini (PyInterpreterState * interp )
100107{
101108 struct atexit_state * state = & interp -> atexit ;
109+ // XXX Can this be unlocked?
110+ _PyAtExit_LOCK (state );
102111 atexit_cleanup (state );
103112 PyMem_Free (state -> callbacks );
104113 state -> callbacks = NULL ;
@@ -114,6 +123,7 @@ _PyAtExit_Fini(PyInterpreterState *interp)
114123 PyMem_Free (callback );
115124 exitfunc (data );
116125 }
126+ _PyAtExit_UNLOCK (state );
117127}
118128
119129
155165_PyAtExit_Call (PyInterpreterState * interp )
156166{
157167 struct atexit_state * state = & interp -> atexit ;
168+ _PyAtExit_LOCK (state );
158169 atexit_callfuncs (state );
170+ _PyAtExit_UNLOCK (state );
159171}
160172
161173
@@ -192,31 +204,39 @@ atexit_register(PyObject *module, PyObject *args, PyObject *kwargs)
192204 }
193205
194206 struct atexit_state * state = get_atexit_state ();
207+ /* In theory, we could hold the lock for a shorter amount of time
208+ * using some fancy compare-exchanges with the length. However, I'm lazy.
209+ */
210+ _PyAtExit_LOCK (state );
195211 if (state -> ncallbacks >= state -> callback_len ) {
196212 atexit_py_callback * * r ;
197213 state -> callback_len += 16 ;
198214 size_t size = sizeof (atexit_py_callback * ) * (size_t )state -> callback_len ;
199215 r = (atexit_py_callback * * )PyMem_Realloc (state -> callbacks , size );
200216 if (r == NULL ) {
217+ _PyAtExit_UNLOCK (state );
201218 return PyErr_NoMemory ();
202219 }
203220 state -> callbacks = r ;
204221 }
205222
206223 atexit_py_callback * callback = PyMem_Malloc (sizeof (atexit_py_callback ));
207224 if (callback == NULL ) {
225+ _PyAtExit_UNLOCK (state );
208226 return PyErr_NoMemory ();
209227 }
210228
211229 callback -> args = PyTuple_GetSlice (args , 1 , PyTuple_GET_SIZE (args ));
212230 if (callback -> args == NULL ) {
213231 PyMem_Free (callback );
232+ _PyAtExit_UNLOCK (state );
214233 return NULL ;
215234 }
216235 callback -> func = Py_NewRef (func );
217236 callback -> kwargs = Py_XNewRef (kwargs );
218237
219238 state -> callbacks [state -> ncallbacks ++ ] = callback ;
239+ _PyAtExit_UNLOCK (state );
220240
221241 return Py_NewRef (func );
222242}
@@ -233,7 +253,9 @@ static PyObject *
233253atexit_run_exitfuncs (PyObject * module , PyObject * unused )
234254{
235255 struct atexit_state * state = get_atexit_state ();
256+ _PyAtExit_LOCK (state );
236257 atexit_callfuncs (state );
258+ _PyAtExit_UNLOCK (state );
237259 Py_RETURN_NONE ;
238260}
239261
@@ -246,7 +268,10 @@ Clear the list of previously registered exit functions.");
246268static PyObject *
247269atexit_clear (PyObject * module , PyObject * unused )
248270{
249- atexit_cleanup (get_atexit_state ());
271+ struct atexit_state * state = get_atexit_state ();
272+ _PyAtExit_LOCK (state );
273+ atexit_cleanup (state );
274+ _PyAtExit_UNLOCK (state );
250275 Py_RETURN_NONE ;
251276}
252277
@@ -260,7 +285,10 @@ static PyObject *
260285atexit_ncallbacks (PyObject * module , PyObject * unused )
261286{
262287 struct atexit_state * state = get_atexit_state ();
263- return PyLong_FromSsize_t (state -> ncallbacks );
288+ _PyAtExit_LOCK (state );
289+ PyObject * res = PyLong_FromSsize_t (state -> ncallbacks );
290+ _PyAtExit_UNLOCK (state );
291+ return res ;
264292}
265293
266294PyDoc_STRVAR (atexit_unregister__doc__ ,
@@ -276,21 +304,28 @@ static PyObject *
276304atexit_unregister (PyObject * module , PyObject * func )
277305{
278306 struct atexit_state * state = get_atexit_state ();
307+ _PyAtExit_LOCK (state );
279308 for (int i = 0 ; i < state -> ncallbacks ; i ++ )
280309 {
281310 atexit_py_callback * cb = state -> callbacks [i ];
282311 if (cb == NULL ) {
283312 continue ;
284313 }
285314
286- int eq = PyObject_RichCompareBool (cb -> func , func , Py_EQ );
315+ // We need to hold our own reference to this
316+ // in case another thread is trying to unregister as well.
317+ PyObject * to_compare = cb -> func ;
318+ _PyAtExit_UNLOCK (state );
319+ int eq = PyObject_RichCompareBool (to_compare , func , Py_EQ );
287320 if (eq < 0 ) {
288321 return NULL ;
289322 }
323+ _PyAtExit_LOCK (state );
290324 if (eq ) {
291325 atexit_delete_cb (state , i );
292326 }
293327 }
328+ _PyAtExit_UNLOCK (state );
294329 Py_RETURN_NONE ;
295330}
296331
0 commit comments