/src/cpython/Modules/atexitmodule.c
Line | Count | Source (jump to first uncovered line) |
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_atexit.h" // export _Py_AtExit() |
11 | | #include "pycore_initconfig.h" // _PyStatus_NO_MEMORY |
12 | | #include "pycore_interp.h" // PyInterpreterState.atexit |
13 | | #include "pycore_pystate.h" // _PyInterpreterState_GET |
14 | | |
15 | | /* ===================================================================== */ |
16 | | /* Callback machinery. */ |
17 | | |
18 | | static inline struct atexit_state* |
19 | | get_atexit_state(void) |
20 | 0 | { |
21 | 0 | PyInterpreterState *interp = _PyInterpreterState_GET(); |
22 | 0 | return &interp->atexit; |
23 | 0 | } |
24 | | |
25 | | |
26 | | int |
27 | | PyUnstable_AtExit(PyInterpreterState *interp, |
28 | | atexit_datacallbackfunc func, void *data) |
29 | 0 | { |
30 | 0 | PyThreadState *tstate = _PyThreadState_GET(); |
31 | 0 | _Py_EnsureTstateNotNULL(tstate); |
32 | 0 | assert(tstate->interp == interp); |
33 | |
|
34 | 0 | atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback)); |
35 | 0 | if (callback == NULL) { |
36 | 0 | PyErr_NoMemory(); |
37 | 0 | return -1; |
38 | 0 | } |
39 | 0 | callback->func = func; |
40 | 0 | callback->data = data; |
41 | 0 | callback->next = NULL; |
42 | |
|
43 | 0 | struct atexit_state *state = &interp->atexit; |
44 | 0 | _PyAtExit_LockCallbacks(state); |
45 | 0 | atexit_callback *top = state->ll_callbacks; |
46 | 0 | if (top == NULL) { |
47 | 0 | state->ll_callbacks = callback; |
48 | 0 | } |
49 | 0 | else { |
50 | 0 | callback->next = top; |
51 | 0 | state->ll_callbacks = callback; |
52 | 0 | } |
53 | 0 | _PyAtExit_UnlockCallbacks(state); |
54 | 0 | return 0; |
55 | 0 | } |
56 | | |
57 | | |
58 | | /* Clear all callbacks without calling them */ |
59 | | static void |
60 | | atexit_cleanup(struct atexit_state *state) |
61 | 0 | { |
62 | 0 | PyList_Clear(state->callbacks); |
63 | 0 | } |
64 | | |
65 | | |
66 | | PyStatus |
67 | | _PyAtExit_Init(PyInterpreterState *interp) |
68 | 16 | { |
69 | 16 | struct atexit_state *state = &interp->atexit; |
70 | | // _PyAtExit_Init() must only be called once |
71 | 16 | assert(state->callbacks == NULL); |
72 | | |
73 | 16 | state->callbacks = PyList_New(0); |
74 | 16 | if (state->callbacks == NULL) { |
75 | 0 | return _PyStatus_NO_MEMORY(); |
76 | 0 | } |
77 | 16 | return _PyStatus_OK(); |
78 | 16 | } |
79 | | |
80 | | void |
81 | | _PyAtExit_Fini(PyInterpreterState *interp) |
82 | 0 | { |
83 | | // In theory, there shouldn't be any threads left by now, so we |
84 | | // won't lock this. |
85 | 0 | struct atexit_state *state = &interp->atexit; |
86 | 0 | atexit_cleanup(state); |
87 | 0 | Py_CLEAR(state->callbacks); |
88 | |
|
89 | 0 | atexit_callback *next = state->ll_callbacks; |
90 | 0 | state->ll_callbacks = NULL; |
91 | 0 | while (next != NULL) { |
92 | 0 | atexit_callback *callback = next; |
93 | 0 | next = callback->next; |
94 | 0 | atexit_datacallbackfunc exitfunc = callback->func; |
95 | 0 | void *data = callback->data; |
96 | | // It was allocated in _PyAtExit_AddCallback(). |
97 | 0 | PyMem_Free(callback); |
98 | 0 | exitfunc(data); |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | static void |
103 | | atexit_callfuncs(struct atexit_state *state) |
104 | 0 | { |
105 | 0 | assert(!PyErr_Occurred()); |
106 | 0 | assert(state->callbacks != NULL); |
107 | 0 | assert(PyList_CheckExact(state->callbacks)); |
108 | | |
109 | | // Create a copy of the list for thread safety |
110 | 0 | PyObject *copy = PyList_GetSlice(state->callbacks, 0, PyList_GET_SIZE(state->callbacks)); |
111 | 0 | if (copy == NULL) |
112 | 0 | { |
113 | 0 | PyErr_FormatUnraisable("Exception ignored while " |
114 | 0 | "copying atexit callbacks"); |
115 | 0 | return; |
116 | 0 | } |
117 | | |
118 | 0 | for (Py_ssize_t i = 0; i < PyList_GET_SIZE(copy); ++i) { |
119 | | // We don't have to worry about evil borrowed references, because |
120 | | // no other threads can access this list. |
121 | 0 | PyObject *tuple = PyList_GET_ITEM(copy, i); |
122 | 0 | assert(PyTuple_CheckExact(tuple)); |
123 | |
|
124 | 0 | PyObject *func = PyTuple_GET_ITEM(tuple, 0); |
125 | 0 | PyObject *args = PyTuple_GET_ITEM(tuple, 1); |
126 | 0 | PyObject *kwargs = PyTuple_GET_ITEM(tuple, 2); |
127 | |
|
128 | 0 | PyObject *res = PyObject_Call(func, |
129 | 0 | args, |
130 | 0 | kwargs == Py_None ? NULL : kwargs); |
131 | 0 | if (res == NULL) { |
132 | 0 | PyErr_FormatUnraisable( |
133 | 0 | "Exception ignored in atexit callback %R", func); |
134 | 0 | } |
135 | 0 | else { |
136 | 0 | Py_DECREF(res); |
137 | 0 | } |
138 | 0 | } |
139 | |
|
140 | 0 | Py_DECREF(copy); |
141 | 0 | atexit_cleanup(state); |
142 | |
|
143 | 0 | assert(!PyErr_Occurred()); |
144 | 0 | } |
145 | | |
146 | | |
147 | | void |
148 | | _PyAtExit_Call(PyInterpreterState *interp) |
149 | 0 | { |
150 | 0 | struct atexit_state *state = &interp->atexit; |
151 | 0 | atexit_callfuncs(state); |
152 | 0 | } |
153 | | |
154 | | |
155 | | /* ===================================================================== */ |
156 | | /* Module methods. */ |
157 | | |
158 | | |
159 | | PyDoc_STRVAR(atexit_register__doc__, |
160 | | "register($module, func, /, *args, **kwargs)\n\ |
161 | | --\n\ |
162 | | \n\ |
163 | | Register a function to be executed upon normal program termination\n\ |
164 | | \n\ |
165 | | func - function to be called at exit\n\ |
166 | | args - optional arguments to pass to func\n\ |
167 | | kwargs - optional keyword arguments to pass to func\n\ |
168 | | \n\ |
169 | | func is returned to facilitate usage as a decorator."); |
170 | | |
171 | | static PyObject * |
172 | | atexit_register(PyObject *module, PyObject *args, PyObject *kwargs) |
173 | 0 | { |
174 | 0 | if (PyTuple_GET_SIZE(args) == 0) { |
175 | 0 | PyErr_SetString(PyExc_TypeError, |
176 | 0 | "register() takes at least 1 argument (0 given)"); |
177 | 0 | return NULL; |
178 | 0 | } |
179 | | |
180 | 0 | PyObject *func = PyTuple_GET_ITEM(args, 0); |
181 | 0 | if (!PyCallable_Check(func)) { |
182 | 0 | PyErr_SetString(PyExc_TypeError, |
183 | 0 | "the first argument must be callable"); |
184 | 0 | return NULL; |
185 | 0 | } |
186 | 0 | PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); |
187 | 0 | PyObject *func_kwargs = kwargs; |
188 | |
|
189 | 0 | if (func_kwargs == NULL) |
190 | 0 | { |
191 | 0 | func_kwargs = Py_None; |
192 | 0 | } |
193 | 0 | PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs); |
194 | 0 | if (callback == NULL) |
195 | 0 | { |
196 | 0 | return NULL; |
197 | 0 | } |
198 | | |
199 | 0 | struct atexit_state *state = get_atexit_state(); |
200 | | // atexit callbacks go in a LIFO order |
201 | 0 | if (PyList_Insert(state->callbacks, 0, callback) < 0) |
202 | 0 | { |
203 | 0 | Py_DECREF(callback); |
204 | 0 | return NULL; |
205 | 0 | } |
206 | 0 | Py_DECREF(callback); |
207 | |
|
208 | 0 | return Py_NewRef(func); |
209 | 0 | } |
210 | | |
211 | | PyDoc_STRVAR(atexit_run_exitfuncs__doc__, |
212 | | "_run_exitfuncs($module, /)\n\ |
213 | | --\n\ |
214 | | \n\ |
215 | | Run all registered exit functions.\n\ |
216 | | \n\ |
217 | | If a callback raises an exception, it is logged with sys.unraisablehook."); |
218 | | |
219 | | static PyObject * |
220 | | atexit_run_exitfuncs(PyObject *module, PyObject *Py_UNUSED(dummy)) |
221 | 0 | { |
222 | 0 | struct atexit_state *state = get_atexit_state(); |
223 | 0 | atexit_callfuncs(state); |
224 | 0 | Py_RETURN_NONE; |
225 | 0 | } |
226 | | |
227 | | PyDoc_STRVAR(atexit_clear__doc__, |
228 | | "_clear($module, /)\n\ |
229 | | --\n\ |
230 | | \n\ |
231 | | Clear the list of previously registered exit functions."); |
232 | | |
233 | | static PyObject * |
234 | | atexit_clear(PyObject *module, PyObject *Py_UNUSED(dummy)) |
235 | 0 | { |
236 | 0 | atexit_cleanup(get_atexit_state()); |
237 | 0 | Py_RETURN_NONE; |
238 | 0 | } |
239 | | |
240 | | PyDoc_STRVAR(atexit_ncallbacks__doc__, |
241 | | "_ncallbacks($module, /)\n\ |
242 | | --\n\ |
243 | | \n\ |
244 | | Return the number of registered exit functions."); |
245 | | |
246 | | static PyObject * |
247 | | atexit_ncallbacks(PyObject *module, PyObject *Py_UNUSED(dummy)) |
248 | 0 | { |
249 | 0 | struct atexit_state *state = get_atexit_state(); |
250 | 0 | assert(state->callbacks != NULL); |
251 | 0 | assert(PyList_CheckExact(state->callbacks)); |
252 | 0 | return PyLong_FromSsize_t(PyList_GET_SIZE(state->callbacks)); |
253 | 0 | } |
254 | | |
255 | | static int |
256 | | atexit_unregister_locked(PyObject *callbacks, PyObject *func) |
257 | 0 | { |
258 | 0 | for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) { |
259 | 0 | PyObject *tuple = PyList_GET_ITEM(callbacks, i); |
260 | 0 | assert(PyTuple_CheckExact(tuple)); |
261 | 0 | PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0); |
262 | 0 | int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ); |
263 | 0 | if (cmp < 0) |
264 | 0 | { |
265 | 0 | return -1; |
266 | 0 | } |
267 | 0 | if (cmp == 1) { |
268 | | // We found a callback! |
269 | 0 | if (PyList_SetSlice(callbacks, i, i + 1, NULL) < 0) { |
270 | 0 | return -1; |
271 | 0 | } |
272 | 0 | --i; |
273 | 0 | } |
274 | 0 | } |
275 | | |
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | | PyDoc_STRVAR(atexit_unregister__doc__, |
280 | | "unregister($module, func, /)\n\ |
281 | | --\n\ |
282 | | \n\ |
283 | | Unregister an exit function which was previously registered using\n\ |
284 | | atexit.register\n\ |
285 | | \n\ |
286 | | func - function to be unregistered"); |
287 | | |
288 | | static PyObject * |
289 | | atexit_unregister(PyObject *module, PyObject *func) |
290 | 0 | { |
291 | 0 | struct atexit_state *state = get_atexit_state(); |
292 | 0 | int result; |
293 | 0 | Py_BEGIN_CRITICAL_SECTION(state->callbacks); |
294 | 0 | result = atexit_unregister_locked(state->callbacks, func); |
295 | 0 | Py_END_CRITICAL_SECTION(); |
296 | 0 | return result < 0 ? NULL : Py_None; |
297 | 0 | } |
298 | | |
299 | | |
300 | | static PyMethodDef atexit_methods[] = { |
301 | | {"register", _PyCFunction_CAST(atexit_register), METH_VARARGS|METH_KEYWORDS, |
302 | | atexit_register__doc__}, |
303 | | {"_clear", atexit_clear, METH_NOARGS, atexit_clear__doc__}, |
304 | | {"unregister", atexit_unregister, METH_O, atexit_unregister__doc__}, |
305 | | {"_run_exitfuncs", atexit_run_exitfuncs, METH_NOARGS, |
306 | | atexit_run_exitfuncs__doc__}, |
307 | | {"_ncallbacks", atexit_ncallbacks, METH_NOARGS, |
308 | | atexit_ncallbacks__doc__}, |
309 | | {NULL, NULL} /* sentinel */ |
310 | | }; |
311 | | |
312 | | |
313 | | /* ===================================================================== */ |
314 | | /* Initialization function. */ |
315 | | |
316 | | PyDoc_STRVAR(atexit__doc__, |
317 | | "allow programmer to define multiple exit functions to be executed\n\ |
318 | | upon normal program termination.\n\ |
319 | | \n\ |
320 | | Two public functions, register and unregister, are defined.\n\ |
321 | | "); |
322 | | |
323 | | static PyModuleDef_Slot atexitmodule_slots[] = { |
324 | | {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, |
325 | | {Py_mod_gil, Py_MOD_GIL_NOT_USED}, |
326 | | {0, NULL} |
327 | | }; |
328 | | |
329 | | static struct PyModuleDef atexitmodule = { |
330 | | PyModuleDef_HEAD_INIT, |
331 | | .m_name = "atexit", |
332 | | .m_doc = atexit__doc__, |
333 | | .m_size = 0, |
334 | | .m_methods = atexit_methods, |
335 | | .m_slots = atexitmodule_slots, |
336 | | }; |
337 | | |
338 | | PyMODINIT_FUNC |
339 | | PyInit_atexit(void) |
340 | 0 | { |
341 | 0 | return PyModuleDef_Init(&atexitmodule); |
342 | 0 | } |