• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*
get_atexit_state(void)18 get_atexit_state(void)
19 {
20     PyInterpreterState *interp = _PyInterpreterState_GET();
21     return &interp->atexit;
22 }
23 
24 
25 static void
atexit_delete_cb(struct atexit_state * state,int i)26 atexit_delete_cb(struct atexit_state *state, int i)
27 {
28     atexit_callback *cb = state->callbacks[i];
29     state->callbacks[i] = NULL;
30 
31     Py_DECREF(cb->func);
32     Py_DECREF(cb->args);
33     Py_XDECREF(cb->kwargs);
34     PyMem_Free(cb);
35 }
36 
37 
38 /* Clear all callbacks without calling them */
39 static void
atexit_cleanup(struct atexit_state * state)40 atexit_cleanup(struct atexit_state *state)
41 {
42     atexit_callback *cb;
43     for (int i = 0; i < state->ncallbacks; i++) {
44         cb = state->callbacks[i];
45         if (cb == NULL)
46             continue;
47 
48         atexit_delete_cb(state, i);
49     }
50     state->ncallbacks = 0;
51 }
52 
53 
54 PyStatus
_PyAtExit_Init(PyInterpreterState * interp)55 _PyAtExit_Init(PyInterpreterState *interp)
56 {
57     struct atexit_state *state = &interp->atexit;
58     // _PyAtExit_Init() must only be called once
59     assert(state->callbacks == NULL);
60 
61     state->callback_len = 32;
62     state->ncallbacks = 0;
63     state->callbacks = PyMem_New(atexit_callback*, state->callback_len);
64     if (state->callbacks == NULL) {
65         return _PyStatus_NO_MEMORY();
66     }
67     return _PyStatus_OK();
68 }
69 
70 
71 void
_PyAtExit_Fini(PyInterpreterState * interp)72 _PyAtExit_Fini(PyInterpreterState *interp)
73 {
74     struct atexit_state *state = &interp->atexit;
75     atexit_cleanup(state);
76     PyMem_Free(state->callbacks);
77     state->callbacks = NULL;
78 }
79 
80 
81 static void
atexit_callfuncs(struct atexit_state * state)82 atexit_callfuncs(struct atexit_state *state)
83 {
84     assert(!PyErr_Occurred());
85 
86     if (state->ncallbacks == 0) {
87         return;
88     }
89 
90     for (int i = state->ncallbacks - 1; i >= 0; i--) {
91         atexit_callback *cb = state->callbacks[i];
92         if (cb == NULL) {
93             continue;
94         }
95 
96         // bpo-46025: Increment the refcount of cb->func as the call itself may unregister it
97         PyObject* the_func = Py_NewRef(cb->func);
98         PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);
99         if (res == NULL) {
100             _PyErr_WriteUnraisableMsg("in atexit callback", the_func);
101         }
102         else {
103             Py_DECREF(res);
104         }
105         Py_DECREF(the_func);
106     }
107 
108     atexit_cleanup(state);
109 
110     assert(!PyErr_Occurred());
111 }
112 
113 
114 void
_PyAtExit_Call(PyInterpreterState * interp)115 _PyAtExit_Call(PyInterpreterState *interp)
116 {
117     struct atexit_state *state = &interp->atexit;
118     atexit_callfuncs(state);
119 }
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 *
atexit_register(PyObject * module,PyObject * args,PyObject * kwargs)138 atexit_register(PyObject *module, PyObject *args, PyObject *kwargs)
139 {
140     if (PyTuple_GET_SIZE(args) == 0) {
141         PyErr_SetString(PyExc_TypeError,
142                 "register() takes at least 1 argument (0 given)");
143         return NULL;
144     }
145 
146     PyObject *func = PyTuple_GET_ITEM(args, 0);
147     if (!PyCallable_Check(func)) {
148         PyErr_SetString(PyExc_TypeError,
149                 "the first argument must be callable");
150         return NULL;
151     }
152 
153     struct atexit_state *state = get_atexit_state();
154     if (state->ncallbacks >= state->callback_len) {
155         atexit_callback **r;
156         state->callback_len += 16;
157         size_t size = sizeof(atexit_callback*) * (size_t)state->callback_len;
158         r = (atexit_callback**)PyMem_Realloc(state->callbacks, size);
159         if (r == NULL) {
160             return PyErr_NoMemory();
161         }
162         state->callbacks = r;
163     }
164 
165     atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback));
166     if (callback == NULL) {
167         return PyErr_NoMemory();
168     }
169 
170     callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
171     if (callback->args == NULL) {
172         PyMem_Free(callback);
173         return NULL;
174     }
175     callback->func = Py_NewRef(func);
176     callback->kwargs = Py_XNewRef(kwargs);
177 
178     state->callbacks[state->ncallbacks++] = callback;
179 
180     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 callaback raises an exception, it is logged with sys.unraisablehook.");
189 
190 static PyObject *
atexit_run_exitfuncs(PyObject * module,PyObject * unused)191 atexit_run_exitfuncs(PyObject *module, PyObject *unused)
192 {
193     struct atexit_state *state = get_atexit_state();
194     atexit_callfuncs(state);
195     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 *
atexit_clear(PyObject * module,PyObject * unused)204 atexit_clear(PyObject *module, PyObject *unused)
205 {
206     atexit_cleanup(get_atexit_state());
207     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 *
atexit_ncallbacks(PyObject * module,PyObject * unused)216 atexit_ncallbacks(PyObject *module, PyObject *unused)
217 {
218     struct atexit_state *state = get_atexit_state();
219     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 *
atexit_unregister(PyObject * module,PyObject * func)231 atexit_unregister(PyObject *module, PyObject *func)
232 {
233     struct atexit_state *state = get_atexit_state();
234     for (int i = 0; i < state->ncallbacks; i++)
235     {
236         atexit_callback *cb = state->callbacks[i];
237         if (cb == NULL) {
238             continue;
239         }
240 
241         int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
242         if (eq < 0) {
243             return NULL;
244         }
245         if (eq) {
246             atexit_delete_cb(state, i);
247         }
248     }
249     Py_RETURN_NONE;
250 }
251 
252 
253 static PyMethodDef atexit_methods[] = {
254     {"register", (PyCFunction)(void(*)(void)) 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
PyInit_atexit(void)287 PyInit_atexit(void)
288 {
289     return PyModuleDef_Init(&atexitmodule);
290 }
291