@@ -3338,6 +3338,54 @@ pyeval_getlocals(PyObject *module, PyObject *Py_UNUSED(args))
3338
3338
return Py_XNewRef (PyEval_GetLocals ());
3339
3339
}
3340
3340
3341
+ struct atexit_data {
3342
+ int called ;
3343
+ PyThreadState * tstate ;
3344
+ PyInterpreterState * interp ;
3345
+ };
3346
+
3347
+ static void
3348
+ atexit_callback (void * data )
3349
+ {
3350
+ struct atexit_data * at_data = (struct atexit_data * )data ;
3351
+ // Ensure that the callback is from the same interpreter
3352
+ assert (PyThreadState_Get () == at_data -> tstate );
3353
+ assert (PyInterpreterState_Get () == at_data -> interp );
3354
+ ++ at_data -> called ;
3355
+ }
3356
+
3357
+ static PyObject *
3358
+ test_atexit (PyObject * self , PyObject * Py_UNUSED (args ))
3359
+ {
3360
+ PyThreadState * oldts = PyThreadState_Swap (NULL );
3361
+ PyThreadState * tstate = Py_NewInterpreter ();
3362
+
3363
+ struct atexit_data data = {0 };
3364
+ data .tstate = PyThreadState_Get ();
3365
+ data .interp = PyInterpreterState_Get ();
3366
+
3367
+ int amount = 10 ;
3368
+ for (int i = 0 ; i < amount ; ++ i )
3369
+ {
3370
+ int res = PyUnstable_AtExit (tstate -> interp , atexit_callback , (void * )& data );
3371
+ if (res < 0 ) {
3372
+ Py_EndInterpreter (tstate );
3373
+ PyThreadState_Swap (oldts );
3374
+ PyErr_SetString (PyExc_RuntimeError , "atexit callback failed" );
3375
+ return NULL ;
3376
+ }
3377
+ }
3378
+
3379
+ Py_EndInterpreter (tstate );
3380
+ PyThreadState_Swap (oldts );
3381
+
3382
+ if (data .called != amount ) {
3383
+ PyErr_SetString (PyExc_RuntimeError , "atexit callback not called" );
3384
+ return NULL ;
3385
+ }
3386
+ Py_RETURN_NONE ;
3387
+ }
3388
+
3341
3389
static PyMethodDef TestMethods [] = {
3342
3390
{"set_errno" , set_errno , METH_VARARGS },
3343
3391
{"test_config" , test_config , METH_NOARGS },
@@ -3483,6 +3531,7 @@ static PyMethodDef TestMethods[] = {
3483
3531
{"function_set_warning" , function_set_warning , METH_NOARGS },
3484
3532
{"test_critical_sections" , test_critical_sections , METH_NOARGS },
3485
3533
{"pyeval_getlocals" , pyeval_getlocals , METH_NOARGS },
3534
+ {"test_atexit" , test_atexit , METH_NOARGS },
3486
3535
{NULL , NULL } /* sentinel */
3487
3536
};
3488
3537
0 commit comments