Skip to content

Commit 830ebc3

Browse files
committed
Fix merge conflicts.
1 parent acf05aa commit 830ebc3

File tree

7 files changed

+72
-39
lines changed

7 files changed

+72
-39
lines changed

Doc/c-api/init.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,15 @@ Initializing and finalizing the interpreter
557557
customized Python that always runs in isolated mode using
558558
:c:func:`Py_RunMain`.
559559
560+
.. c:function:: int PyUnstable_AtExit(PyInterpreterState *interp, void (*func)(void *), void *data)
561+
562+
Register an :mod:`atexit` callback for the target interpreter *interp*.
563+
This is similar to :c:func:`Py_AtExit`, but takes an explicit interpreter and
564+
data pointer for the callback.
565+
566+
The :term:`GIL` must be held for *interp*.
567+
568+
.. versionadded:: 3.13
560569
561570
Process-wide parameters
562571
=======================

Doc/c-api/sys.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,7 @@ Process Control
426426
function registered last is called first. Each cleanup function will be called
427427
at most once. Since Python's internal finalization will have completed before
428428
the cleanup function, no Python APIs should be called by *func*.
429+
430+
.. seealso::
431+
432+
:c:func:`PyUnstable_AtExit` for passing a ``void *data`` argument.

Include/internal/pycore_atexit.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ typedef struct {
4444

4545
struct atexit_state {
4646
atexit_callback *ll_callbacks;
47-
atexit_callback *last_ll_callback;
4847

4948
// XXX The rest of the state could be moved to the atexit module state
5049
// and a low-level callback added for it during module exec.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix loss of callbacks after more than one call to
2+
:c:func:`PyUnstable_AtExit`.

Modules/_testcapimodule.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3338,6 +3338,54 @@ pyeval_getlocals(PyObject *module, PyObject *Py_UNUSED(args))
33383338
return Py_XNewRef(PyEval_GetLocals());
33393339
}
33403340

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+
33413389
static PyMethodDef TestMethods[] = {
33423390
{"set_errno", set_errno, METH_VARARGS},
33433391
{"test_config", test_config, METH_NOARGS},
@@ -3483,6 +3531,7 @@ static PyMethodDef TestMethods[] = {
34833531
{"function_set_warning", function_set_warning, METH_NOARGS},
34843532
{"test_critical_sections", test_critical_sections, METH_NOARGS},
34853533
{"pyeval_getlocals", pyeval_getlocals, METH_NOARGS},
3534+
{"test_atexit", test_atexit, METH_NOARGS},
34863535
{NULL, NULL} /* sentinel */
34873536
};
34883537

Modules/_testinternalcapi.c

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,39 +1234,6 @@ unicode_transformdecimalandspacetoascii(PyObject *self, PyObject *arg)
12341234
return _PyUnicode_TransformDecimalAndSpaceToASCII(arg);
12351235
}
12361236

1237-
1238-
struct atexit_data {
1239-
int called;
1240-
};
1241-
1242-
static void
1243-
callback(void *data)
1244-
{
1245-
((struct atexit_data *)data)->called += 1;
1246-
}
1247-
1248-
static PyObject *
1249-
test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
1250-
{
1251-
PyThreadState *oldts = PyThreadState_Swap(NULL);
1252-
PyThreadState *tstate = Py_NewInterpreter();
1253-
1254-
struct atexit_data data = {0};
1255-
int res = PyUnstable_AtExit(tstate->interp, callback, (void *)&data);
1256-
Py_EndInterpreter(tstate);
1257-
PyThreadState_Swap(oldts);
1258-
if (res < 0) {
1259-
return NULL;
1260-
}
1261-
1262-
if (data.called == 0) {
1263-
PyErr_SetString(PyExc_RuntimeError, "atexit callback not called");
1264-
return NULL;
1265-
}
1266-
Py_RETURN_NONE;
1267-
}
1268-
1269-
12701237
static PyObject *
12711238
test_pyobject_is_freed(const char *test_name, PyObject *op)
12721239
{
@@ -2065,7 +2032,6 @@ static PyMethodDef module_functions[] = {
20652032
{"_PyTraceMalloc_GetTraceback", tracemalloc_get_traceback, METH_VARARGS},
20662033
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
20672034
{"_PyUnicode_TransformDecimalAndSpaceToASCII", unicode_transformdecimalandspacetoascii, METH_O},
2068-
{"test_atexit", test_atexit, METH_NOARGS},
20692035
{"check_pyobject_forbidden_bytes_is_freed",
20702036
check_pyobject_forbidden_bytes_is_freed, METH_NOARGS},
20712037
{"check_pyobject_freed_is_freed", check_pyobject_freed_is_freed, METH_NOARGS},

Modules/atexitmodule.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ int
2727
PyUnstable_AtExit(PyInterpreterState *interp,
2828
atexit_datacallbackfunc func, void *data)
2929
{
30-
assert(interp == _PyInterpreterState_GET());
30+
PyThreadState *tstate = _PyThreadState_GET();
31+
_Py_EnsureTstateNotNULL(tstate);
32+
assert(tstate->interp == interp);
33+
3134
atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback));
3235
if (callback == NULL) {
3336
PyErr_NoMemory();
@@ -38,12 +41,13 @@ PyUnstable_AtExit(PyInterpreterState *interp,
3841
callback->next = NULL;
3942

4043
struct atexit_state *state = &interp->atexit;
41-
if (state->ll_callbacks == NULL) {
44+
atexit_callback *top = state->ll_callbacks;
45+
if (top == NULL) {
4246
state->ll_callbacks = callback;
43-
state->last_ll_callback = callback;
4447
}
4548
else {
46-
state->last_ll_callback->next = callback;
49+
callback->next = top;
50+
state->ll_callbacks = callback;
4751
}
4852
return 0;
4953
}

0 commit comments

Comments
 (0)