/src/cpython/Modules/atexitmodule.c
Line | Count | Source |
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 | 4 | { |
21 | 4 | PyInterpreterState *interp = _PyInterpreterState_GET(); |
22 | 4 | return &interp->atexit; |
23 | 4 | } |
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 | 32 | { |
69 | 32 | struct atexit_state *state = &interp->atexit; |
70 | | // _PyAtExit_Init() must only be called once |
71 | 32 | assert(state->callbacks == NULL); |
72 | | |
73 | 32 | state->callbacks = PyList_New(0); |
74 | 32 | if (state->callbacks == NULL) { |
75 | 0 | return _PyStatus_NO_MEMORY(); |
76 | 0 | } |
77 | 32 | return _PyStatus_OK(); |
78 | 32 | } |
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 | atexit_cleanup(state); |
116 | 0 | return; |
117 | 0 | } |
118 | | |
119 | 0 | for (Py_ssize_t i = 0; i < PyList_GET_SIZE(copy); ++i) { |
120 | | // We don't have to worry about evil borrowed references, because |
121 | | // no other threads can access this list. |
122 | 0 | PyObject *tuple = PyList_GET_ITEM(copy, i); |
123 | 0 | assert(PyTuple_CheckExact(tuple)); |
124 | |
|
125 | 0 | PyObject *func = PyTuple_GET_ITEM(tuple, 0); |
126 | 0 | PyObject *args = PyTuple_GET_ITEM(tuple, 1); |
127 | 0 | PyObject *kwargs = PyTuple_GET_ITEM(tuple, 2); |
128 | |
|
129 | 0 | PyObject *res = PyObject_Call(func, |
130 | 0 | args, |
131 | 0 | kwargs == Py_None ? NULL : kwargs); |
132 | 0 | if (res == NULL) { |
133 | 0 | PyErr_FormatUnraisable( |
134 | 0 | "Exception ignored in atexit callback %R", func); |
135 | 0 | } |
136 | 0 | else { |
137 | 0 | Py_DECREF(res); |
138 | 0 | } |
139 | 0 | } |
140 | |
|
141 | 0 | Py_DECREF(copy); |
142 | 0 | atexit_cleanup(state); |
143 | |
|
144 | 0 | assert(!PyErr_Occurred()); |
145 | 0 | } |
146 | | |
147 | | |
148 | | void |
149 | | _PyAtExit_Call(PyInterpreterState *interp) |
150 | 0 | { |
151 | 0 | struct atexit_state *state = &interp->atexit; |
152 | 0 | atexit_callfuncs(state); |
153 | 0 | } |
154 | | |
155 | | |
156 | | /* ===================================================================== */ |
157 | | /* Module methods. */ |
158 | | |
159 | | |
160 | | PyDoc_STRVAR(atexit_register__doc__, |
161 | | "register($module, func, /, *args, **kwargs)\n\ |
162 | | --\n\ |
163 | | \n\ |
164 | | Register a function to be executed upon normal program termination\n\ |
165 | | \n\ |
166 | | func - function to be called at exit\n\ |
167 | | args - optional arguments to pass to func\n\ |
168 | | kwargs - optional keyword arguments to pass to func\n\ |
169 | | \n\ |
170 | | func is returned to facilitate usage as a decorator."); |
171 | | |
172 | | static PyObject * |
173 | | atexit_register(PyObject *module, PyObject *args, PyObject *kwargs) |
174 | 4 | { |
175 | 4 | if (PyTuple_GET_SIZE(args) == 0) { |
176 | 0 | PyErr_SetString(PyExc_TypeError, |
177 | 0 | "register() takes at least 1 argument (0 given)"); |
178 | 0 | return NULL; |
179 | 0 | } |
180 | | |
181 | 4 | PyObject *func = PyTuple_GET_ITEM(args, 0); |
182 | 4 | if (!PyCallable_Check(func)) { |
183 | 0 | PyErr_SetString(PyExc_TypeError, |
184 | 0 | "the first argument must be callable"); |
185 | 0 | return NULL; |
186 | 0 | } |
187 | 4 | PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); |
188 | 4 | if (func_args == NULL) { |
189 | 0 | return NULL; |
190 | 0 | } |
191 | 4 | PyObject *func_kwargs = kwargs; |
192 | | |
193 | 4 | if (func_kwargs == NULL) |
194 | 4 | { |
195 | 4 | func_kwargs = Py_None; |
196 | 4 | } |
197 | 4 | PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs); |
198 | 4 | Py_DECREF(func_args); |
199 | 4 | if (callback == NULL) |
200 | 0 | { |
201 | 0 | return NULL; |
202 | 0 | } |
203 | | |
204 | 4 | struct atexit_state *state = get_atexit_state(); |
205 | | // atexit callbacks go in a LIFO order |
206 | 4 | if (PyList_Insert(state->callbacks, 0, callback) < 0) |
207 | 0 | { |
208 | 0 | Py_DECREF(callback); |
209 | 0 | return NULL; |
210 | 0 | } |
211 | 4 | Py_DECREF(callback); |
212 | | |
213 | 4 | return Py_NewRef(func); |
214 | 4 | } |
215 | | |
216 | | PyDoc_STRVAR(atexit_run_exitfuncs__doc__, |
217 | | "_run_exitfuncs($module, /)\n\ |
218 | | --\n\ |
219 | | \n\ |
220 | | Run all registered exit functions.\n\ |
221 | | \n\ |
222 | | If a callback raises an exception, it is logged with sys.unraisablehook."); |
223 | | |
224 | | static PyObject * |
225 | | atexit_run_exitfuncs(PyObject *module, PyObject *Py_UNUSED(dummy)) |
226 | 0 | { |
227 | 0 | struct atexit_state *state = get_atexit_state(); |
228 | 0 | atexit_callfuncs(state); |
229 | 0 | Py_RETURN_NONE; |
230 | 0 | } |
231 | | |
232 | | PyDoc_STRVAR(atexit_clear__doc__, |
233 | | "_clear($module, /)\n\ |
234 | | --\n\ |
235 | | \n\ |
236 | | Clear the list of previously registered exit functions."); |
237 | | |
238 | | static PyObject * |
239 | | atexit_clear(PyObject *module, PyObject *Py_UNUSED(dummy)) |
240 | 0 | { |
241 | 0 | atexit_cleanup(get_atexit_state()); |
242 | 0 | Py_RETURN_NONE; |
243 | 0 | } |
244 | | |
245 | | PyDoc_STRVAR(atexit_ncallbacks__doc__, |
246 | | "_ncallbacks($module, /)\n\ |
247 | | --\n\ |
248 | | \n\ |
249 | | Return the number of registered exit functions."); |
250 | | |
251 | | static PyObject * |
252 | | atexit_ncallbacks(PyObject *module, PyObject *Py_UNUSED(dummy)) |
253 | 0 | { |
254 | 0 | struct atexit_state *state = get_atexit_state(); |
255 | 0 | assert(state->callbacks != NULL); |
256 | 0 | assert(PyList_CheckExact(state->callbacks)); |
257 | 0 | return PyLong_FromSsize_t(PyList_GET_SIZE(state->callbacks)); |
258 | 0 | } |
259 | | |
260 | | static int |
261 | | atexit_unregister_locked(PyObject *callbacks, PyObject *func) |
262 | 0 | { |
263 | 0 | for (Py_ssize_t i = PyList_GET_SIZE(callbacks) - 1; i >= 0; --i) { |
264 | 0 | PyObject *tuple = Py_NewRef(PyList_GET_ITEM(callbacks, i)); |
265 | 0 | assert(PyTuple_CheckExact(tuple)); |
266 | 0 | PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0); |
267 | 0 | int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ); |
268 | 0 | if (cmp < 0) { |
269 | 0 | Py_DECREF(tuple); |
270 | 0 | return -1; |
271 | 0 | } |
272 | 0 | if (cmp == 1) { |
273 | | // We found a callback! |
274 | | // But its index could have changed if it or other callbacks were |
275 | | // unregistered during the comparison. |
276 | 0 | Py_ssize_t j = PyList_GET_SIZE(callbacks) - 1; |
277 | 0 | j = Py_MIN(j, i); |
278 | 0 | for (; j >= 0; --j) { |
279 | 0 | if (PyList_GET_ITEM(callbacks, j) == tuple) { |
280 | | // We found the callback index! For real! |
281 | 0 | if (PyList_SetSlice(callbacks, j, j + 1, NULL) < 0) { |
282 | 0 | Py_DECREF(tuple); |
283 | 0 | return -1; |
284 | 0 | } |
285 | 0 | i = j; |
286 | 0 | break; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | } |
290 | 0 | Py_DECREF(tuple); |
291 | 0 | if (i >= PyList_GET_SIZE(callbacks)) { |
292 | 0 | i = PyList_GET_SIZE(callbacks); |
293 | 0 | } |
294 | 0 | } |
295 | | |
296 | 0 | return 0; |
297 | 0 | } |
298 | | |
299 | | PyDoc_STRVAR(atexit_unregister__doc__, |
300 | | "unregister($module, func, /)\n\ |
301 | | --\n\ |
302 | | \n\ |
303 | | Unregister an exit function which was previously registered using\n\ |
304 | | atexit.register\n\ |
305 | | \n\ |
306 | | func - function to be unregistered"); |
307 | | |
308 | | static PyObject * |
309 | | atexit_unregister(PyObject *module, PyObject *func) |
310 | 0 | { |
311 | 0 | struct atexit_state *state = get_atexit_state(); |
312 | 0 | int result; |
313 | 0 | Py_BEGIN_CRITICAL_SECTION(state->callbacks); |
314 | 0 | result = atexit_unregister_locked(state->callbacks, func); |
315 | 0 | Py_END_CRITICAL_SECTION(); |
316 | 0 | return result < 0 ? NULL : Py_None; |
317 | 0 | } |
318 | | |
319 | | |
320 | | static PyMethodDef atexit_methods[] = { |
321 | | {"register", _PyCFunction_CAST(atexit_register), METH_VARARGS|METH_KEYWORDS, |
322 | | atexit_register__doc__}, |
323 | | {"_clear", atexit_clear, METH_NOARGS, atexit_clear__doc__}, |
324 | | {"unregister", atexit_unregister, METH_O, atexit_unregister__doc__}, |
325 | | {"_run_exitfuncs", atexit_run_exitfuncs, METH_NOARGS, |
326 | | atexit_run_exitfuncs__doc__}, |
327 | | {"_ncallbacks", atexit_ncallbacks, METH_NOARGS, |
328 | | atexit_ncallbacks__doc__}, |
329 | | {NULL, NULL} /* sentinel */ |
330 | | }; |
331 | | |
332 | | |
333 | | /* ===================================================================== */ |
334 | | /* Initialization function. */ |
335 | | |
336 | | PyDoc_STRVAR(atexit__doc__, |
337 | | "allow programmer to define multiple exit functions to be executed\n\ |
338 | | upon normal program termination.\n\ |
339 | | \n\ |
340 | | Two public functions, register and unregister, are defined.\n\ |
341 | | "); |
342 | | |
343 | | static PyModuleDef_Slot atexitmodule_slots[] = { |
344 | | {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, |
345 | | {Py_mod_gil, Py_MOD_GIL_NOT_USED}, |
346 | | {0, NULL} |
347 | | }; |
348 | | |
349 | | static struct PyModuleDef atexitmodule = { |
350 | | PyModuleDef_HEAD_INIT, |
351 | | .m_name = "atexit", |
352 | | .m_doc = atexit__doc__, |
353 | | .m_size = 0, |
354 | | .m_methods = atexit_methods, |
355 | | .m_slots = atexitmodule_slots, |
356 | | }; |
357 | | |
358 | | PyMODINIT_FUNC |
359 | | PyInit_atexit(void) |
360 | 4 | { |
361 | 4 | return PyModuleDef_Init(&atexitmodule); |
362 | 4 | } |