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