LCOV - code coverage report
Current view: | top level - Modules - atexitmodule.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 63 | 102 | 61.8 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 9 | 13 | 69.2 %
|  |  | Branches: | 16 | 36 | 44.4 %

               Branch data     Line data    Source code
           1                 :            : /*
           2                 :            :  *  atexit - allow programmer to define multiple exit functions to be executed
           3                 :            :  *  upon normal program termination.
           4                 :            :  *
           5                 :            :  *   Translated from atexit.py by Collin Winter.
           6                 :            :  +   Copyright 2007 Python Software Foundation.
           7                 :            :  */
           8                 :            :
           9                 :            : #include "Python.h"
          10                 :            : #include "pycore_initconfig.h"    // _PyStatus_NO_MEMORY
          11                 :            : #include "pycore_interp.h"        // PyInterpreterState.atexit
          12                 :            : #include "pycore_pystate.h"       // _PyInterpreterState_GET
          13                 :            :
          14                 :            : /* ===================================================================== */
          15                 :            : /* Callback machinery. */
          16                 :            :
          17                 :            : static inline struct atexit_state*
          18                 :          2 : get_atexit_state(void)
          19                 :            : {
          20                 :          2 :     PyInterpreterState *interp = _PyInterpreterState_GET();
          21                 :          2 :     return &interp->atexit;
          22                 :            : }
          23                 :            :
          24                 :            :
          25                 :            : static void
          26                 :          2 : atexit_delete_cb(struct atexit_state *state, int i)
          27                 :            : {
          28                 :          2 :     atexit_callback *cb = state->callbacks[i];
          29                 :          2 :     state->callbacks[i] = NULL;
          30                 :            :
          31                 :          2 :     Py_DECREF(cb->func);
          32                 :          2 :     Py_DECREF(cb->args);
          33                 :          2 :     Py_XDECREF(cb->kwargs);
          34                 :          2 :     PyMem_Free(cb);
          35                 :          2 : }
          36                 :            :
          37                 :            :
          38                 :            : /* Clear all callbacks without calling them */
          39                 :            : static void
          40                 :         27 : atexit_cleanup(struct atexit_state *state)
          41                 :            : {
          42                 :            :     atexit_callback *cb;
          43         [ +  + ]:         29 :     for (int i = 0; i < state->ncallbacks; i++) {
          44                 :          2 :         cb = state->callbacks[i];
          45         [ -  + ]:          2 :         if (cb == NULL)
          46                 :          0 :             continue;
          47                 :            :
          48                 :          2 :         atexit_delete_cb(state, i);
          49                 :            :     }
          50                 :         27 :     state->ncallbacks = 0;
          51                 :         27 : }
          52                 :            :
          53                 :            :
          54                 :            : PyStatus
          55                 :         29 : _PyAtExit_Init(PyInterpreterState *interp)
          56                 :            : {
          57                 :         29 :     struct atexit_state *state = &interp->atexit;
          58                 :            :     // _PyAtExit_Init() must only be called once
          59                 :            :     assert(state->callbacks == NULL);
          60                 :            :
          61                 :         29 :     state->callback_len = 32;
          62                 :         29 :     state->ncallbacks = 0;
          63         [ +  - ]:         29 :     state->callbacks = PyMem_New(atexit_callback*, state->callback_len);
          64         [ -  + ]:         29 :     if (state->callbacks == NULL) {
          65                 :          0 :         return _PyStatus_NO_MEMORY();
          66                 :            :     }
          67                 :         29 :     return _PyStatus_OK();
          68                 :            : }
          69                 :            :
          70                 :            :
          71                 :            : void
          72                 :         25 : _PyAtExit_Fini(PyInterpreterState *interp)
          73                 :            : {
          74                 :         25 :     struct atexit_state *state = &interp->atexit;
          75                 :         25 :     atexit_cleanup(state);
          76                 :         25 :     PyMem_Free(state->callbacks);
          77                 :         25 :     state->callbacks = NULL;
          78                 :         25 : }
          79                 :            :
          80                 :            :
          81                 :            : static void
          82                 :         25 : atexit_callfuncs(struct atexit_state *state)
          83                 :            : {
          84                 :            :     assert(!PyErr_Occurred());
          85                 :            :
          86         [ +  + ]:         25 :     if (state->ncallbacks == 0) {
          87                 :         23 :         return;
          88                 :            :     }
          89                 :            :
          90         [ +  + ]:          4 :     for (int i = state->ncallbacks - 1; i >= 0; i--) {
          91                 :          2 :         atexit_callback *cb = state->callbacks[i];
          92         [ -  + ]:          2 :         if (cb == NULL) {
          93                 :          0 :             continue;
          94                 :            :         }
          95                 :            :
          96                 :            :         // bpo-46025: Increment the refcount of cb->func as the call itself may unregister it
          97                 :          2 :         PyObject* the_func = Py_NewRef(cb->func);
          98                 :          2 :         PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);
          99         [ -  + ]:          2 :         if (res == NULL) {
         100                 :          0 :             _PyErr_WriteUnraisableMsg("in atexit callback", the_func);
         101                 :            :         }
         102                 :            :         else {
         103                 :          2 :             Py_DECREF(res);
         104                 :            :         }
         105                 :          2 :         Py_DECREF(the_func);
         106                 :            :     }
         107                 :            :
         108                 :          2 :     atexit_cleanup(state);
         109                 :            :
         110                 :            :     assert(!PyErr_Occurred());
         111                 :            : }
         112                 :            :
         113                 :            :
         114                 :            : void
         115                 :         25 : _PyAtExit_Call(PyInterpreterState *interp)
         116                 :            : {
         117                 :         25 :     struct atexit_state *state = &interp->atexit;
         118                 :         25 :     atexit_callfuncs(state);
         119                 :         25 : }
         120                 :            :
         121                 :            :
         122                 :            : /* ===================================================================== */
         123                 :            : /* Module methods. */
         124                 :            :
         125                 :            :
         126                 :            : PyDoc_STRVAR(atexit_register__doc__,
         127                 :            : "register(func, *args, **kwargs) -> func\n\
         128                 :            : \n\
         129                 :            : Register a function to be executed upon normal program termination\n\
         130                 :            : \n\
         131                 :            :     func - function to be called at exit\n\
         132                 :            :     args - optional arguments to pass to func\n\
         133                 :            :     kwargs - optional keyword arguments to pass to func\n\
         134                 :            : \n\
         135                 :            :     func is returned to facilitate usage as a decorator.");
         136                 :            :
         137                 :            : static PyObject *
         138                 :          2 : atexit_register(PyObject *module, PyObject *args, PyObject *kwargs)
         139                 :            : {
         140         [ -  + ]:          2 :     if (PyTuple_GET_SIZE(args) == 0) {
         141                 :          0 :         PyErr_SetString(PyExc_TypeError,
         142                 :            :                 "register() takes at least 1 argument (0 given)");
         143                 :          0 :         return NULL;
         144                 :            :     }
         145                 :            :
         146                 :          2 :     PyObject *func = PyTuple_GET_ITEM(args, 0);
         147         [ -  + ]:          2 :     if (!PyCallable_Check(func)) {
         148                 :          0 :         PyErr_SetString(PyExc_TypeError,
         149                 :            :                 "the first argument must be callable");
         150                 :          0 :         return NULL;
         151                 :            :     }
         152                 :            :
         153                 :          2 :     struct atexit_state *state = get_atexit_state();
         154         [ -  + ]:          2 :     if (state->ncallbacks >= state->callback_len) {
         155                 :            :         atexit_callback **r;
         156                 :          0 :         state->callback_len += 16;
         157                 :          0 :         size_t size = sizeof(atexit_callback*) * (size_t)state->callback_len;
         158                 :          0 :         r = (atexit_callback**)PyMem_Realloc(state->callbacks, size);
         159         [ #  # ]:          0 :         if (r == NULL) {
         160                 :          0 :             return PyErr_NoMemory();
         161                 :            :         }
         162                 :          0 :         state->callbacks = r;
         163                 :            :     }
         164                 :            :
         165                 :          2 :     atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback));
         166         [ -  + ]:          2 :     if (callback == NULL) {
         167                 :          0 :         return PyErr_NoMemory();
         168                 :            :     }
         169                 :            :
         170                 :          2 :     callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
         171         [ -  + ]:          2 :     if (callback->args == NULL) {
         172                 :          0 :         PyMem_Free(callback);
         173                 :          0 :         return NULL;
         174                 :            :     }
         175                 :          2 :     callback->func = Py_NewRef(func);
         176                 :          2 :     callback->kwargs = Py_XNewRef(kwargs);
         177                 :            :
         178                 :          2 :     state->callbacks[state->ncallbacks++] = callback;
         179                 :            :
         180                 :          2 :     return Py_NewRef(func);
         181                 :            : }
         182                 :            :
         183                 :            : PyDoc_STRVAR(atexit_run_exitfuncs__doc__,
         184                 :            : "_run_exitfuncs() -> None\n\
         185                 :            : \n\
         186                 :            : Run all registered exit functions.\n\
         187                 :            : \n\
         188                 :            : If a callback raises an exception, it is logged with sys.unraisablehook.");
         189                 :            :
         190                 :            : static PyObject *
         191                 :          0 : atexit_run_exitfuncs(PyObject *module, PyObject *unused)
         192                 :            : {
         193                 :          0 :     struct atexit_state *state = get_atexit_state();
         194                 :          0 :     atexit_callfuncs(state);
         195                 :          0 :     Py_RETURN_NONE;
         196                 :            : }
         197                 :            :
         198                 :            : PyDoc_STRVAR(atexit_clear__doc__,
         199                 :            : "_clear() -> None\n\
         200                 :            : \n\
         201                 :            : Clear the list of previously registered exit functions.");
         202                 :            :
         203                 :            : static PyObject *
         204                 :          0 : atexit_clear(PyObject *module, PyObject *unused)
         205                 :            : {
         206                 :          0 :     atexit_cleanup(get_atexit_state());
         207                 :          0 :     Py_RETURN_NONE;
         208                 :            : }
         209                 :            :
         210                 :            : PyDoc_STRVAR(atexit_ncallbacks__doc__,
         211                 :            : "_ncallbacks() -> int\n\
         212                 :            : \n\
         213                 :            : Return the number of registered exit functions.");
         214                 :            :
         215                 :            : static PyObject *
         216                 :          0 : atexit_ncallbacks(PyObject *module, PyObject *unused)
         217                 :            : {
         218                 :          0 :     struct atexit_state *state = get_atexit_state();
         219                 :          0 :     return PyLong_FromSsize_t(state->ncallbacks);
         220                 :            : }
         221                 :            :
         222                 :            : PyDoc_STRVAR(atexit_unregister__doc__,
         223                 :            : "unregister(func) -> None\n\
         224                 :            : \n\
         225                 :            : Unregister an exit function which was previously registered using\n\
         226                 :            : atexit.register\n\
         227                 :            : \n\
         228                 :            :     func - function to be unregistered");
         229                 :            :
         230                 :            : static PyObject *
         231                 :          0 : atexit_unregister(PyObject *module, PyObject *func)
         232                 :            : {
         233                 :          0 :     struct atexit_state *state = get_atexit_state();
         234         [ #  # ]:          0 :     for (int i = 0; i < state->ncallbacks; i++)
         235                 :            :     {
         236                 :          0 :         atexit_callback *cb = state->callbacks[i];
         237         [ #  # ]:          0 :         if (cb == NULL) {
         238                 :          0 :             continue;
         239                 :            :         }
         240                 :            :
         241                 :          0 :         int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
         242         [ #  # ]:          0 :         if (eq < 0) {
         243                 :          0 :             return NULL;
         244                 :            :         }
         245         [ #  # ]:          0 :         if (eq) {
         246                 :          0 :             atexit_delete_cb(state, i);
         247                 :            :         }
         248                 :            :     }
         249                 :          0 :     Py_RETURN_NONE;
         250                 :            : }
         251                 :            :
         252                 :            :
         253                 :            : static PyMethodDef atexit_methods[] = {
         254                 :            :     {"register", _PyCFunction_CAST(atexit_register), METH_VARARGS|METH_KEYWORDS,
         255                 :            :         atexit_register__doc__},
         256                 :            :     {"_clear", (PyCFunction) atexit_clear, METH_NOARGS,
         257                 :            :         atexit_clear__doc__},
         258                 :            :     {"unregister", (PyCFunction) atexit_unregister, METH_O,
         259                 :            :         atexit_unregister__doc__},
         260                 :            :     {"_run_exitfuncs", (PyCFunction) atexit_run_exitfuncs, METH_NOARGS,
         261                 :            :         atexit_run_exitfuncs__doc__},
         262                 :            :     {"_ncallbacks", (PyCFunction) atexit_ncallbacks, METH_NOARGS,
         263                 :            :         atexit_ncallbacks__doc__},
         264                 :            :     {NULL, NULL}        /* sentinel */
         265                 :            : };
         266                 :            :
         267                 :            :
         268                 :            : /* ===================================================================== */
         269                 :            : /* Initialization function. */
         270                 :            :
         271                 :            : PyDoc_STRVAR(atexit__doc__,
         272                 :            : "allow programmer to define multiple exit functions to be executed\n\
         273                 :            : upon normal program termination.\n\
         274                 :            : \n\
         275                 :            : Two public functions, register and unregister, are defined.\n\
         276                 :            : ");
         277                 :            :
         278                 :            : static struct PyModuleDef atexitmodule = {
         279                 :            :     PyModuleDef_HEAD_INIT,
         280                 :            :     .m_name = "atexit",
         281                 :            :     .m_doc = atexit__doc__,
         282                 :            :     .m_size = 0,
         283                 :            :     .m_methods = atexit_methods,
         284                 :            : };
         285                 :            :
         286                 :            : PyMODINIT_FUNC
         287                 :          3 : PyInit_atexit(void)
         288                 :            : {
         289                 :          3 :     return PyModuleDef_Init(&atexitmodule);
         290                 :            : }

Generated by: LCOV version 1.14