atexitmodule.c 8.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 *  atexit - allow programmer to define multiple exit functions to be executed
 *  upon normal program termination.
 *
 *   Translated from atexit.py by Collin Winter.
 +   Copyright 2007 Python Software Foundation.
 */

#include "Python.h"

11
/* Forward declaration (for atexit_cleanup) */
12 13 14
static PyObject *atexit_clear(PyObject*, PyObject*);
/* Forward declaration of module object */
static struct PyModuleDef atexitmodule;
15

16 17 18 19 20 21 22 23 24
/* ===================================================================== */
/* Callback machinery. */

typedef struct {
    PyObject *func;
    PyObject *args;
    PyObject *kwargs;
} atexit_callback;

25 26 27 28 29 30 31 32
typedef struct {
    atexit_callback **atexit_callbacks;
    int ncallbacks;
    int callback_len;
} atexitmodule_state;

#define GET_ATEXIT_STATE(mod) ((atexitmodule_state*)PyModule_GetState(mod))

33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
static void
atexit_delete_cb(atexitmodule_state *modstate, int i)
{
    atexit_callback *cb;

    cb = modstate->atexit_callbacks[i];
    modstate->atexit_callbacks[i] = NULL;
    Py_DECREF(cb->func);
    Py_DECREF(cb->args);
    Py_XDECREF(cb->kwargs);
    PyMem_Free(cb);
}

/* Clear all callbacks without calling them */
static void
atexit_cleanup(atexitmodule_state *modstate)
{
    atexit_callback *cb;
    int i;
    for (i = 0; i < modstate->ncallbacks; i++) {
        cb = modstate->atexit_callbacks[i];
        if (cb == NULL)
            continue;

        atexit_delete_cb(modstate, i);
    }
    modstate->ncallbacks = 0;
}

63
/* Installed into pylifecycle.c's atexit mechanism */
64

65
static void
66 67 68 69
atexit_callfuncs(void)
{
    PyObject *exc_type = NULL, *exc_value, *exc_tb, *r;
    atexit_callback *cb;
70 71
    PyObject *module;
    atexitmodule_state *modstate;
72
    int i;
73 74 75

    module = PyState_FindModule(&atexitmodule);
    if (module == NULL)
76
        return;
77 78 79 80 81 82 83
    modstate = GET_ATEXIT_STATE(module);

    if (modstate->ncallbacks == 0)
        return;


    for (i = modstate->ncallbacks - 1; i >= 0; i--)
84
    {
85
        cb = modstate->atexit_callbacks[i];
86 87 88 89 90 91
        if (cb == NULL)
            continue;

        r = PyObject_Call(cb->func, cb->args, cb->kwargs);
        Py_XDECREF(r);
        if (r == NULL) {
Neal Norwitz's avatar
Neal Norwitz committed
92 93
            /* Maintain the last exception, but don't leak if there are
               multiple exceptions. */
94 95
            if (exc_type) {
                Py_DECREF(exc_type);
Neal Norwitz's avatar
Neal Norwitz committed
96
                Py_XDECREF(exc_value);
97
                Py_XDECREF(exc_tb);
98 99 100 101
            }
            PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
            if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
                PySys_WriteStderr("Error in atexit._run_exitfuncs:\n");
102
                PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb);
103 104 105 106
                PyErr_Display(exc_type, exc_value, exc_tb);
            }
        }
    }
107

108
    atexit_cleanup(modstate);
109

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    if (exc_type)
        PyErr_Restore(exc_type, exc_value, exc_tb);
}

/* ===================================================================== */
/* Module methods. */

PyDoc_STRVAR(atexit_register__doc__,
"register(func, *args, **kwargs) -> func\n\
\n\
Register a function to be executed upon normal program termination\n\
\n\
    func - function to be called at exit\n\
    args - optional arguments to pass to func\n\
    kwargs - optional keyword arguments to pass to func\n\
\n\
    func is returned to facilitate usage as a decorator.");

static PyObject *
atexit_register(PyObject *self, PyObject *args, PyObject *kwargs)
{
131
    atexitmodule_state *modstate;
132 133
    atexit_callback *new_callback;
    PyObject *func = NULL;
134 135 136 137

    modstate = GET_ATEXIT_STATE(self);

    if (modstate->ncallbacks >= modstate->callback_len) {
138
        atexit_callback **r;
139 140 141
        modstate->callback_len += 16;
        r = (atexit_callback**)PyMem_Realloc(modstate->atexit_callbacks,
                                      sizeof(atexit_callback*) * modstate->callback_len);
142 143
        if (r == NULL)
            return PyErr_NoMemory();
144
        modstate->atexit_callbacks = r;
145
    }
146

147 148 149
    if (PyTuple_GET_SIZE(args) == 0) {
        PyErr_SetString(PyExc_TypeError,
                "register() takes at least 1 argument (0 given)");
150
        return NULL;
151
    }
152

153 154 155 156 157 158
    func = PyTuple_GET_ITEM(args, 0);
    if (!PyCallable_Check(func)) {
        PyErr_SetString(PyExc_TypeError,
                "the first argument must be callable");
        return NULL;
    }
159

160 161
    new_callback = PyMem_Malloc(sizeof(atexit_callback));
    if (new_callback == NULL)
162
        return PyErr_NoMemory();
163 164 165 166 167 168 169 170 171 172

    new_callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
    if (new_callback->args == NULL) {
        PyMem_Free(new_callback);
        return NULL;
    }
    new_callback->func = func;
    new_callback->kwargs = kwargs;
    Py_INCREF(func);
    Py_XINCREF(kwargs);
173 174 175

    modstate->atexit_callbacks[modstate->ncallbacks++] = new_callback;

176 177 178 179
    Py_INCREF(func);
    return func;
}

Skip Montanaro's avatar
Skip Montanaro committed
180 181 182 183 184
PyDoc_STRVAR(atexit_run_exitfuncs__doc__,
"_run_exitfuncs() -> None\n\
\n\
Run all registered exit functions.");

185
static PyObject *
186
atexit_run_exitfuncs(PyObject *self, PyObject *unused)
187 188 189 190 191 192 193
{
    atexit_callfuncs();
    if (PyErr_Occurred())
        return NULL;
    Py_RETURN_NONE;
}

Skip Montanaro's avatar
Skip Montanaro committed
194 195 196 197 198
PyDoc_STRVAR(atexit_clear__doc__,
"_clear() -> None\n\
\n\
Clear the list of previously registered exit functions.");

199
static PyObject *
200
atexit_clear(PyObject *self, PyObject *unused)
201 202 203 204 205 206 207 208 209 210 211 212
{
    atexit_cleanup(GET_ATEXIT_STATE(self));
    Py_RETURN_NONE;
}

PyDoc_STRVAR(atexit_ncallbacks__doc__,
"_ncallbacks() -> int\n\
\n\
Return the number of registered exit functions.");

static PyObject *
atexit_ncallbacks(PyObject *self, PyObject *unused)
213
{
214 215 216 217
    atexitmodule_state *modstate;

    modstate = GET_ATEXIT_STATE(self);

218 219 220 221 222 223 224 225 226 227
    return PyLong_FromSsize_t(modstate->ncallbacks);
}

static int
atexit_m_traverse(PyObject *self, visitproc visit, void *arg)
{
    int i;
    atexitmodule_state *modstate;

    modstate = GET_ATEXIT_STATE(self);
228
    for (i = 0; i < modstate->ncallbacks; i++) {
229
        atexit_callback *cb = modstate->atexit_callbacks[i];
230 231
        if (cb == NULL)
            continue;
232 233 234
        Py_VISIT(cb->func);
        Py_VISIT(cb->args);
        Py_VISIT(cb->kwargs);
235
    }
236 237 238 239 240 241 242 243 244 245
    return 0;
}

static int
atexit_m_clear(PyObject *self)
{
    atexitmodule_state *modstate;
    modstate = GET_ATEXIT_STATE(self);
    atexit_cleanup(modstate);
    return 0;
246 247
}

248 249 250 251 252
static void
atexit_free(PyObject *m)
{
    atexitmodule_state *modstate;
    modstate = GET_ATEXIT_STATE(m);
253
    atexit_cleanup(modstate);
254 255 256
    PyMem_Free(modstate->atexit_callbacks);
}

Skip Montanaro's avatar
Skip Montanaro committed
257 258 259
PyDoc_STRVAR(atexit_unregister__doc__,
"unregister(func) -> None\n\
\n\
260
Unregister an exit function which was previously registered using\n\
Skip Montanaro's avatar
Skip Montanaro committed
261 262 263 264
atexit.register\n\
\n\
    func - function to be unregistered");

265 266 267
static PyObject *
atexit_unregister(PyObject *self, PyObject *func)
{
268
    atexitmodule_state *modstate;
269 270
    atexit_callback *cb;
    int i, eq;
271 272 273 274

    modstate = GET_ATEXIT_STATE(self);

    for (i = 0; i < modstate->ncallbacks; i++)
275
    {
276
        cb = modstate->atexit_callbacks[i];
277 278
        if (cb == NULL)
            continue;
279

280 281 282 283
        eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
        if (eq < 0)
            return NULL;
        if (eq)
284
            atexit_delete_cb(modstate, i);
285 286 287 288 289 290 291 292
    }
    Py_RETURN_NONE;
}

static PyMethodDef atexit_methods[] = {
    {"register", (PyCFunction) atexit_register, METH_VARARGS|METH_KEYWORDS,
        atexit_register__doc__},
    {"_clear", (PyCFunction) atexit_clear, METH_NOARGS,
Skip Montanaro's avatar
Skip Montanaro committed
293
        atexit_clear__doc__},
294
    {"unregister", (PyCFunction) atexit_unregister, METH_O,
Skip Montanaro's avatar
Skip Montanaro committed
295
        atexit_unregister__doc__},
296
    {"_run_exitfuncs", (PyCFunction) atexit_run_exitfuncs, METH_NOARGS,
Skip Montanaro's avatar
Skip Montanaro committed
297
        atexit_run_exitfuncs__doc__},
298 299
    {"_ncallbacks", (PyCFunction) atexit_ncallbacks, METH_NOARGS,
        atexit_ncallbacks__doc__},
300 301 302 303 304 305 306
    {NULL, NULL}        /* sentinel */
};

/* ===================================================================== */
/* Initialization function. */

PyDoc_STRVAR(atexit__doc__,
Skip Montanaro's avatar
Skip Montanaro committed
307
"allow programmer to define multiple exit functions to be executed\
308 309
upon normal program termination.\n\
\n\
Skip Montanaro's avatar
Skip Montanaro committed
310
Two public functions, register and unregister, are defined.\n\
311 312
");

313 314

static struct PyModuleDef atexitmodule = {
315 316 317 318 319 320 321 322 323
    PyModuleDef_HEAD_INIT,
    "atexit",
    atexit__doc__,
    sizeof(atexitmodule_state),
    atexit_methods,
    NULL,
    atexit_m_traverse,
    atexit_m_clear,
    (freefunc)atexit_free
324 325
};

326
PyMODINIT_FUNC
327
PyInit_atexit(void)
328 329
{
    PyObject *m;
330
    atexitmodule_state *modstate;
331

332
    m = PyModule_Create(&atexitmodule);
333
    if (m == NULL)
334
        return NULL;
335 336 337 338

    modstate = GET_ATEXIT_STATE(m);
    modstate->callback_len = 32;
    modstate->ncallbacks = 0;
339
    modstate->atexit_callbacks = PyMem_New(atexit_callback*,
340 341 342 343
                                           modstate->callback_len);
    if (modstate->atexit_callbacks == NULL)
        return NULL;

344
    _Py_PyAtExit(atexit_callfuncs);
345
    return m;
346
}