xref: /third_party/python/Modules/atexitmodule.c (revision 7db96d56)
17db96d56Sopenharmony_ci/*
27db96d56Sopenharmony_ci *  atexit - allow programmer to define multiple exit functions to be executed
37db96d56Sopenharmony_ci *  upon normal program termination.
47db96d56Sopenharmony_ci *
57db96d56Sopenharmony_ci *   Translated from atexit.py by Collin Winter.
67db96d56Sopenharmony_ci +   Copyright 2007 Python Software Foundation.
77db96d56Sopenharmony_ci */
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci#include "Python.h"
107db96d56Sopenharmony_ci#include "pycore_initconfig.h"    // _PyStatus_NO_MEMORY
117db96d56Sopenharmony_ci#include "pycore_interp.h"        // PyInterpreterState.atexit
127db96d56Sopenharmony_ci#include "pycore_pystate.h"       // _PyInterpreterState_GET
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci/* ===================================================================== */
157db96d56Sopenharmony_ci/* Callback machinery. */
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_cistatic inline struct atexit_state*
187db96d56Sopenharmony_ciget_atexit_state(void)
197db96d56Sopenharmony_ci{
207db96d56Sopenharmony_ci    PyInterpreterState *interp = _PyInterpreterState_GET();
217db96d56Sopenharmony_ci    return &interp->atexit;
227db96d56Sopenharmony_ci}
237db96d56Sopenharmony_ci
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_cistatic void
267db96d56Sopenharmony_ciatexit_delete_cb(struct atexit_state *state, int i)
277db96d56Sopenharmony_ci{
287db96d56Sopenharmony_ci    atexit_callback *cb = state->callbacks[i];
297db96d56Sopenharmony_ci    state->callbacks[i] = NULL;
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci    Py_DECREF(cb->func);
327db96d56Sopenharmony_ci    Py_DECREF(cb->args);
337db96d56Sopenharmony_ci    Py_XDECREF(cb->kwargs);
347db96d56Sopenharmony_ci    PyMem_Free(cb);
357db96d56Sopenharmony_ci}
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_ci/* Clear all callbacks without calling them */
397db96d56Sopenharmony_cistatic void
407db96d56Sopenharmony_ciatexit_cleanup(struct atexit_state *state)
417db96d56Sopenharmony_ci{
427db96d56Sopenharmony_ci    atexit_callback *cb;
437db96d56Sopenharmony_ci    for (int i = 0; i < state->ncallbacks; i++) {
447db96d56Sopenharmony_ci        cb = state->callbacks[i];
457db96d56Sopenharmony_ci        if (cb == NULL)
467db96d56Sopenharmony_ci            continue;
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_ci        atexit_delete_cb(state, i);
497db96d56Sopenharmony_ci    }
507db96d56Sopenharmony_ci    state->ncallbacks = 0;
517db96d56Sopenharmony_ci}
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_ciPyStatus
557db96d56Sopenharmony_ci_PyAtExit_Init(PyInterpreterState *interp)
567db96d56Sopenharmony_ci{
577db96d56Sopenharmony_ci    struct atexit_state *state = &interp->atexit;
587db96d56Sopenharmony_ci    // _PyAtExit_Init() must only be called once
597db96d56Sopenharmony_ci    assert(state->callbacks == NULL);
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci    state->callback_len = 32;
627db96d56Sopenharmony_ci    state->ncallbacks = 0;
637db96d56Sopenharmony_ci    state->callbacks = PyMem_New(atexit_callback*, state->callback_len);
647db96d56Sopenharmony_ci    if (state->callbacks == NULL) {
657db96d56Sopenharmony_ci        return _PyStatus_NO_MEMORY();
667db96d56Sopenharmony_ci    }
677db96d56Sopenharmony_ci    return _PyStatus_OK();
687db96d56Sopenharmony_ci}
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_civoid
727db96d56Sopenharmony_ci_PyAtExit_Fini(PyInterpreterState *interp)
737db96d56Sopenharmony_ci{
747db96d56Sopenharmony_ci    struct atexit_state *state = &interp->atexit;
757db96d56Sopenharmony_ci    atexit_cleanup(state);
767db96d56Sopenharmony_ci    PyMem_Free(state->callbacks);
777db96d56Sopenharmony_ci    state->callbacks = NULL;
787db96d56Sopenharmony_ci}
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_cistatic void
827db96d56Sopenharmony_ciatexit_callfuncs(struct atexit_state *state)
837db96d56Sopenharmony_ci{
847db96d56Sopenharmony_ci    assert(!PyErr_Occurred());
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_ci    if (state->ncallbacks == 0) {
877db96d56Sopenharmony_ci        return;
887db96d56Sopenharmony_ci    }
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci    for (int i = state->ncallbacks - 1; i >= 0; i--) {
917db96d56Sopenharmony_ci        atexit_callback *cb = state->callbacks[i];
927db96d56Sopenharmony_ci        if (cb == NULL) {
937db96d56Sopenharmony_ci            continue;
947db96d56Sopenharmony_ci        }
957db96d56Sopenharmony_ci
967db96d56Sopenharmony_ci        // bpo-46025: Increment the refcount of cb->func as the call itself may unregister it
977db96d56Sopenharmony_ci        PyObject* the_func = Py_NewRef(cb->func);
987db96d56Sopenharmony_ci        PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);
997db96d56Sopenharmony_ci        if (res == NULL) {
1007db96d56Sopenharmony_ci            _PyErr_WriteUnraisableMsg("in atexit callback", the_func);
1017db96d56Sopenharmony_ci        }
1027db96d56Sopenharmony_ci        else {
1037db96d56Sopenharmony_ci            Py_DECREF(res);
1047db96d56Sopenharmony_ci        }
1057db96d56Sopenharmony_ci        Py_DECREF(the_func);
1067db96d56Sopenharmony_ci    }
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ci    atexit_cleanup(state);
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ci    assert(!PyErr_Occurred());
1117db96d56Sopenharmony_ci}
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_civoid
1157db96d56Sopenharmony_ci_PyAtExit_Call(PyInterpreterState *interp)
1167db96d56Sopenharmony_ci{
1177db96d56Sopenharmony_ci    struct atexit_state *state = &interp->atexit;
1187db96d56Sopenharmony_ci    atexit_callfuncs(state);
1197db96d56Sopenharmony_ci}
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci
1227db96d56Sopenharmony_ci/* ===================================================================== */
1237db96d56Sopenharmony_ci/* Module methods. */
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ciPyDoc_STRVAR(atexit_register__doc__,
1277db96d56Sopenharmony_ci"register(func, *args, **kwargs) -> func\n\
1287db96d56Sopenharmony_ci\n\
1297db96d56Sopenharmony_ciRegister a function to be executed upon normal program termination\n\
1307db96d56Sopenharmony_ci\n\
1317db96d56Sopenharmony_ci    func - function to be called at exit\n\
1327db96d56Sopenharmony_ci    args - optional arguments to pass to func\n\
1337db96d56Sopenharmony_ci    kwargs - optional keyword arguments to pass to func\n\
1347db96d56Sopenharmony_ci\n\
1357db96d56Sopenharmony_ci    func is returned to facilitate usage as a decorator.");
1367db96d56Sopenharmony_ci
1377db96d56Sopenharmony_cistatic PyObject *
1387db96d56Sopenharmony_ciatexit_register(PyObject *module, PyObject *args, PyObject *kwargs)
1397db96d56Sopenharmony_ci{
1407db96d56Sopenharmony_ci    if (PyTuple_GET_SIZE(args) == 0) {
1417db96d56Sopenharmony_ci        PyErr_SetString(PyExc_TypeError,
1427db96d56Sopenharmony_ci                "register() takes at least 1 argument (0 given)");
1437db96d56Sopenharmony_ci        return NULL;
1447db96d56Sopenharmony_ci    }
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci    PyObject *func = PyTuple_GET_ITEM(args, 0);
1477db96d56Sopenharmony_ci    if (!PyCallable_Check(func)) {
1487db96d56Sopenharmony_ci        PyErr_SetString(PyExc_TypeError,
1497db96d56Sopenharmony_ci                "the first argument must be callable");
1507db96d56Sopenharmony_ci        return NULL;
1517db96d56Sopenharmony_ci    }
1527db96d56Sopenharmony_ci
1537db96d56Sopenharmony_ci    struct atexit_state *state = get_atexit_state();
1547db96d56Sopenharmony_ci    if (state->ncallbacks >= state->callback_len) {
1557db96d56Sopenharmony_ci        atexit_callback **r;
1567db96d56Sopenharmony_ci        state->callback_len += 16;
1577db96d56Sopenharmony_ci        size_t size = sizeof(atexit_callback*) * (size_t)state->callback_len;
1587db96d56Sopenharmony_ci        r = (atexit_callback**)PyMem_Realloc(state->callbacks, size);
1597db96d56Sopenharmony_ci        if (r == NULL) {
1607db96d56Sopenharmony_ci            return PyErr_NoMemory();
1617db96d56Sopenharmony_ci        }
1627db96d56Sopenharmony_ci        state->callbacks = r;
1637db96d56Sopenharmony_ci    }
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci    atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback));
1667db96d56Sopenharmony_ci    if (callback == NULL) {
1677db96d56Sopenharmony_ci        return PyErr_NoMemory();
1687db96d56Sopenharmony_ci    }
1697db96d56Sopenharmony_ci
1707db96d56Sopenharmony_ci    callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
1717db96d56Sopenharmony_ci    if (callback->args == NULL) {
1727db96d56Sopenharmony_ci        PyMem_Free(callback);
1737db96d56Sopenharmony_ci        return NULL;
1747db96d56Sopenharmony_ci    }
1757db96d56Sopenharmony_ci    callback->func = Py_NewRef(func);
1767db96d56Sopenharmony_ci    callback->kwargs = Py_XNewRef(kwargs);
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ci    state->callbacks[state->ncallbacks++] = callback;
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci    return Py_NewRef(func);
1817db96d56Sopenharmony_ci}
1827db96d56Sopenharmony_ci
1837db96d56Sopenharmony_ciPyDoc_STRVAR(atexit_run_exitfuncs__doc__,
1847db96d56Sopenharmony_ci"_run_exitfuncs() -> None\n\
1857db96d56Sopenharmony_ci\n\
1867db96d56Sopenharmony_ciRun all registered exit functions.\n\
1877db96d56Sopenharmony_ci\n\
1887db96d56Sopenharmony_ciIf a callback raises an exception, it is logged with sys.unraisablehook.");
1897db96d56Sopenharmony_ci
1907db96d56Sopenharmony_cistatic PyObject *
1917db96d56Sopenharmony_ciatexit_run_exitfuncs(PyObject *module, PyObject *unused)
1927db96d56Sopenharmony_ci{
1937db96d56Sopenharmony_ci    struct atexit_state *state = get_atexit_state();
1947db96d56Sopenharmony_ci    atexit_callfuncs(state);
1957db96d56Sopenharmony_ci    Py_RETURN_NONE;
1967db96d56Sopenharmony_ci}
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ciPyDoc_STRVAR(atexit_clear__doc__,
1997db96d56Sopenharmony_ci"_clear() -> None\n\
2007db96d56Sopenharmony_ci\n\
2017db96d56Sopenharmony_ciClear the list of previously registered exit functions.");
2027db96d56Sopenharmony_ci
2037db96d56Sopenharmony_cistatic PyObject *
2047db96d56Sopenharmony_ciatexit_clear(PyObject *module, PyObject *unused)
2057db96d56Sopenharmony_ci{
2067db96d56Sopenharmony_ci    atexit_cleanup(get_atexit_state());
2077db96d56Sopenharmony_ci    Py_RETURN_NONE;
2087db96d56Sopenharmony_ci}
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ciPyDoc_STRVAR(atexit_ncallbacks__doc__,
2117db96d56Sopenharmony_ci"_ncallbacks() -> int\n\
2127db96d56Sopenharmony_ci\n\
2137db96d56Sopenharmony_ciReturn the number of registered exit functions.");
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_cistatic PyObject *
2167db96d56Sopenharmony_ciatexit_ncallbacks(PyObject *module, PyObject *unused)
2177db96d56Sopenharmony_ci{
2187db96d56Sopenharmony_ci    struct atexit_state *state = get_atexit_state();
2197db96d56Sopenharmony_ci    return PyLong_FromSsize_t(state->ncallbacks);
2207db96d56Sopenharmony_ci}
2217db96d56Sopenharmony_ci
2227db96d56Sopenharmony_ciPyDoc_STRVAR(atexit_unregister__doc__,
2237db96d56Sopenharmony_ci"unregister(func) -> None\n\
2247db96d56Sopenharmony_ci\n\
2257db96d56Sopenharmony_ciUnregister an exit function which was previously registered using\n\
2267db96d56Sopenharmony_ciatexit.register\n\
2277db96d56Sopenharmony_ci\n\
2287db96d56Sopenharmony_ci    func - function to be unregistered");
2297db96d56Sopenharmony_ci
2307db96d56Sopenharmony_cistatic PyObject *
2317db96d56Sopenharmony_ciatexit_unregister(PyObject *module, PyObject *func)
2327db96d56Sopenharmony_ci{
2337db96d56Sopenharmony_ci    struct atexit_state *state = get_atexit_state();
2347db96d56Sopenharmony_ci    for (int i = 0; i < state->ncallbacks; i++)
2357db96d56Sopenharmony_ci    {
2367db96d56Sopenharmony_ci        atexit_callback *cb = state->callbacks[i];
2377db96d56Sopenharmony_ci        if (cb == NULL) {
2387db96d56Sopenharmony_ci            continue;
2397db96d56Sopenharmony_ci        }
2407db96d56Sopenharmony_ci
2417db96d56Sopenharmony_ci        int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
2427db96d56Sopenharmony_ci        if (eq < 0) {
2437db96d56Sopenharmony_ci            return NULL;
2447db96d56Sopenharmony_ci        }
2457db96d56Sopenharmony_ci        if (eq) {
2467db96d56Sopenharmony_ci            atexit_delete_cb(state, i);
2477db96d56Sopenharmony_ci        }
2487db96d56Sopenharmony_ci    }
2497db96d56Sopenharmony_ci    Py_RETURN_NONE;
2507db96d56Sopenharmony_ci}
2517db96d56Sopenharmony_ci
2527db96d56Sopenharmony_ci
2537db96d56Sopenharmony_cistatic PyMethodDef atexit_methods[] = {
2547db96d56Sopenharmony_ci    {"register", _PyCFunction_CAST(atexit_register), METH_VARARGS|METH_KEYWORDS,
2557db96d56Sopenharmony_ci        atexit_register__doc__},
2567db96d56Sopenharmony_ci    {"_clear", (PyCFunction) atexit_clear, METH_NOARGS,
2577db96d56Sopenharmony_ci        atexit_clear__doc__},
2587db96d56Sopenharmony_ci    {"unregister", (PyCFunction) atexit_unregister, METH_O,
2597db96d56Sopenharmony_ci        atexit_unregister__doc__},
2607db96d56Sopenharmony_ci    {"_run_exitfuncs", (PyCFunction) atexit_run_exitfuncs, METH_NOARGS,
2617db96d56Sopenharmony_ci        atexit_run_exitfuncs__doc__},
2627db96d56Sopenharmony_ci    {"_ncallbacks", (PyCFunction) atexit_ncallbacks, METH_NOARGS,
2637db96d56Sopenharmony_ci        atexit_ncallbacks__doc__},
2647db96d56Sopenharmony_ci    {NULL, NULL}        /* sentinel */
2657db96d56Sopenharmony_ci};
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_ci
2687db96d56Sopenharmony_ci/* ===================================================================== */
2697db96d56Sopenharmony_ci/* Initialization function. */
2707db96d56Sopenharmony_ci
2717db96d56Sopenharmony_ciPyDoc_STRVAR(atexit__doc__,
2727db96d56Sopenharmony_ci"allow programmer to define multiple exit functions to be executed\n\
2737db96d56Sopenharmony_ciupon normal program termination.\n\
2747db96d56Sopenharmony_ci\n\
2757db96d56Sopenharmony_ciTwo public functions, register and unregister, are defined.\n\
2767db96d56Sopenharmony_ci");
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_cistatic struct PyModuleDef atexitmodule = {
2797db96d56Sopenharmony_ci    PyModuleDef_HEAD_INIT,
2807db96d56Sopenharmony_ci    .m_name = "atexit",
2817db96d56Sopenharmony_ci    .m_doc = atexit__doc__,
2827db96d56Sopenharmony_ci    .m_size = 0,
2837db96d56Sopenharmony_ci    .m_methods = atexit_methods,
2847db96d56Sopenharmony_ci};
2857db96d56Sopenharmony_ci
2867db96d56Sopenharmony_ciPyMODINIT_FUNC
2877db96d56Sopenharmony_ciPyInit_atexit(void)
2887db96d56Sopenharmony_ci{
2897db96d56Sopenharmony_ci    return PyModuleDef_Init(&atexitmodule);
2907db96d56Sopenharmony_ci}
291