gh-101659: Add _Py_AtExit() (gh-103298)

The function is like Py_AtExit() but for a single interpreter.  This is a companion to the atexit module's register() function, taking a C callback instead of a Python one.

We also update the _xxinterpchannels module to use _Py_AtExit(), which is the motivating case.  (This is inspired by pain points felt while working on gh-101660.)
This commit is contained in:
Eric Snow 2023-04-05 18:42:02 -06:00 committed by GitHub
parent 4ec8dd10bd
commit 03089fdccc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 268 additions and 67 deletions

View file

@ -7,6 +7,7 @@
*/
#include "Python.h"
#include "pycore_atexit.h"
#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY
#include "pycore_interp.h" // PyInterpreterState.atexit
#include "pycore_pystate.h" // _PyInterpreterState_GET
@ -22,10 +23,36 @@ get_atexit_state(void)
}
int
_Py_AtExit(PyInterpreterState *interp,
atexit_datacallbackfunc func, void *data)
{
assert(interp == _PyInterpreterState_GET());
atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback));
if (callback == NULL) {
PyErr_NoMemory();
return -1;
}
callback->func = func;
callback->data = data;
callback->next = NULL;
struct atexit_state *state = &interp->atexit;
if (state->ll_callbacks == NULL) {
state->ll_callbacks = callback;
state->last_ll_callback = callback;
}
else {
state->last_ll_callback->next = callback;
}
return 0;
}
static void
atexit_delete_cb(struct atexit_state *state, int i)
{
atexit_callback *cb = state->callbacks[i];
atexit_py_callback *cb = state->callbacks[i];
state->callbacks[i] = NULL;
Py_DECREF(cb->func);
@ -39,7 +66,7 @@ atexit_delete_cb(struct atexit_state *state, int i)
static void
atexit_cleanup(struct atexit_state *state)
{
atexit_callback *cb;
atexit_py_callback *cb;
for (int i = 0; i < state->ncallbacks; i++) {
cb = state->callbacks[i];
if (cb == NULL)
@ -60,7 +87,7 @@ _PyAtExit_Init(PyInterpreterState *interp)
state->callback_len = 32;
state->ncallbacks = 0;
state->callbacks = PyMem_New(atexit_callback*, state->callback_len);
state->callbacks = PyMem_New(atexit_py_callback*, state->callback_len);
if (state->callbacks == NULL) {
return _PyStatus_NO_MEMORY();
}
@ -75,6 +102,18 @@ _PyAtExit_Fini(PyInterpreterState *interp)
atexit_cleanup(state);
PyMem_Free(state->callbacks);
state->callbacks = NULL;
atexit_callback *next = state->ll_callbacks;
state->ll_callbacks = NULL;
while (next != NULL) {
atexit_callback *callback = next;
next = callback->next;
atexit_datacallbackfunc exitfunc = callback->func;
void *data = callback->data;
// It was allocated in _PyAtExit_AddCallback().
PyMem_Free(callback);
exitfunc(data);
}
}
@ -88,7 +127,7 @@ atexit_callfuncs(struct atexit_state *state)
}
for (int i = state->ncallbacks - 1; i >= 0; i--) {
atexit_callback *cb = state->callbacks[i];
atexit_py_callback *cb = state->callbacks[i];
if (cb == NULL) {
continue;
}
@ -152,17 +191,17 @@ atexit_register(PyObject *module, PyObject *args, PyObject *kwargs)
struct atexit_state *state = get_atexit_state();
if (state->ncallbacks >= state->callback_len) {
atexit_callback **r;
atexit_py_callback **r;
state->callback_len += 16;
size_t size = sizeof(atexit_callback*) * (size_t)state->callback_len;
r = (atexit_callback**)PyMem_Realloc(state->callbacks, size);
size_t size = sizeof(atexit_py_callback*) * (size_t)state->callback_len;
r = (atexit_py_callback**)PyMem_Realloc(state->callbacks, size);
if (r == NULL) {
return PyErr_NoMemory();
}
state->callbacks = r;
}
atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback));
atexit_py_callback *callback = PyMem_Malloc(sizeof(atexit_py_callback));
if (callback == NULL) {
return PyErr_NoMemory();
}
@ -233,7 +272,7 @@ atexit_unregister(PyObject *module, PyObject *func)
struct atexit_state *state = get_atexit_state();
for (int i = 0; i < state->ncallbacks; i++)
{
atexit_callback *cb = state->callbacks[i];
atexit_py_callback *cb = state->callbacks[i];
if (cb == NULL) {
continue;
}