/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 | 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 | 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 | 0 | { |
175 | 0 | 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 | 0 | PyObject *func = PyTuple_GET_ITEM(args, 0); |
182 | 0 | 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 | 0 | PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); |
188 | 0 | PyObject *func_kwargs = kwargs; |
189 | |
|
190 | 0 | if (func_kwargs == NULL) |
191 | 0 | { |
192 | 0 | func_kwargs = Py_None; |
193 | 0 | } |
194 | 0 | PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs); |
195 | 0 | if (callback == NULL) |
196 | 0 | { |
197 | 0 | return NULL; |
198 | 0 | } |
199 | | |
200 | 0 | struct atexit_state *state = get_atexit_state(); |
201 | | // atexit callbacks go in a LIFO order |
202 | 0 | if (PyList_Insert(state->callbacks, 0, callback) < 0) |
203 | 0 | { |
204 | 0 | Py_DECREF(callback); |
205 | 0 | return NULL; |
206 | 0 | } |
207 | 0 | Py_DECREF(callback); |
208 | |
|
209 | 0 | return Py_NewRef(func); |
210 | 0 | } |
211 | | |
212 | | PyDoc_STRVAR(atexit_run_exitfuncs__doc__, |
213 | | "_run_exitfuncs($module, /)\n\ |
214 | | --\n\ |
215 | | \n\ |
216 | | Run all registered exit functions.\n\ |
217 | | \n\ |
218 | | If a callback raises an exception, it is logged with sys.unraisablehook."); |
219 | | |
220 | | static PyObject * |
221 | | atexit_run_exitfuncs(PyObject *module, PyObject *Py_UNUSED(dummy)) |
222 | 0 | { |
223 | 0 | struct atexit_state *state = get_atexit_state(); |
224 | 0 | atexit_callfuncs(state); |
225 | 0 | Py_RETURN_NONE; |
226 | 0 | } |
227 | | |
228 | | PyDoc_STRVAR(atexit_clear__doc__, |
229 | | "_clear($module, /)\n\ |
230 | | --\n\ |
231 | | \n\ |
232 | | Clear the list of previously registered exit functions."); |
233 | | |
234 | | static PyObject * |
235 | | atexit_clear(PyObject *module, PyObject *Py_UNUSED(dummy)) |
236 | 0 | { |
237 | 0 | atexit_cleanup(get_atexit_state()); |
238 | 0 | Py_RETURN_NONE; |
239 | 0 | } |
240 | | |
241 | | PyDoc_STRVAR(atexit_ncallbacks__doc__, |
242 | | "_ncallbacks($module, /)\n\ |
243 | | --\n\ |
244 | | \n\ |
245 | | Return the number of registered exit functions."); |
246 | | |
247 | | static PyObject * |
248 | | atexit_ncallbacks(PyObject *module, PyObject *Py_UNUSED(dummy)) |
249 | 0 | { |
250 | 0 | struct atexit_state *state = get_atexit_state(); |
251 | 0 | assert(state->callbacks != NULL); |
252 | 0 | assert(PyList_CheckExact(state->callbacks)); |
253 | 0 | return PyLong_FromSsize_t(PyList_GET_SIZE(state->callbacks)); |
254 | 0 | } |
255 | | |
256 | | static int |
257 | | atexit_unregister_locked(PyObject *callbacks, PyObject *func) |
258 | 0 | { |
259 | 0 | for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) { |
260 | 0 | PyObject *tuple = PyList_GET_ITEM(callbacks, i); |
261 | 0 | assert(PyTuple_CheckExact(tuple)); |
262 | 0 | PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0); |
263 | 0 | int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ); |
264 | 0 | if (cmp < 0) |
265 | 0 | { |
266 | 0 | return -1; |
267 | 0 | } |
268 | 0 | if (cmp == 1) { |
269 | | // We found a callback! |
270 | 0 | if (PyList_SetSlice(callbacks, i, i + 1, NULL) < 0) { |
271 | 0 | return -1; |
272 | 0 | } |
273 | 0 | --i; |
274 | 0 | } |
275 | 0 | } |
276 | | |
277 | 0 | return 0; |
278 | 0 | } |
279 | | |
280 | | PyDoc_STRVAR(atexit_unregister__doc__, |
281 | | "unregister($module, func, /)\n\ |
282 | | --\n\ |
283 | | \n\ |
284 | | Unregister an exit function which was previously registered using\n\ |
285 | | atexit.register\n\ |
286 | | \n\ |
287 | | func - function to be unregistered"); |
288 | | |
289 | | static PyObject * |
290 | | atexit_unregister(PyObject *module, PyObject *func) |
291 | 0 | { |
292 | 0 | struct atexit_state *state = get_atexit_state(); |
293 | 0 | int result; |
294 | 0 | Py_BEGIN_CRITICAL_SECTION(state->callbacks); |
295 | 0 | result = atexit_unregister_locked(state->callbacks, func); |
296 | 0 | Py_END_CRITICAL_SECTION(); |
297 | 0 | return result < 0 ? NULL : Py_None; |
298 | 0 | } |
299 | | |
300 | | |
301 | | static PyMethodDef atexit_methods[] = { |
302 | | {"register", _PyCFunction_CAST(atexit_register), METH_VARARGS|METH_KEYWORDS, |
303 | | atexit_register__doc__}, |
304 | | {"_clear", atexit_clear, METH_NOARGS, atexit_clear__doc__}, |
305 | | {"unregister", atexit_unregister, METH_O, atexit_unregister__doc__}, |
306 | | {"_run_exitfuncs", atexit_run_exitfuncs, METH_NOARGS, |
307 | | atexit_run_exitfuncs__doc__}, |
308 | | {"_ncallbacks", atexit_ncallbacks, METH_NOARGS, |
309 | | atexit_ncallbacks__doc__}, |
310 | | {NULL, NULL} /* sentinel */ |
311 | | }; |
312 | | |
313 | | |
314 | | /* ===================================================================== */ |
315 | | /* Initialization function. */ |
316 | | |
317 | | PyDoc_STRVAR(atexit__doc__, |
318 | | "allow programmer to define multiple exit functions to be executed\n\ |
319 | | upon normal program termination.\n\ |
320 | | \n\ |
321 | | Two public functions, register and unregister, are defined.\n\ |
322 | | "); |
323 | | |
324 | | static PyModuleDef_Slot atexitmodule_slots[] = { |
325 | | {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, |
326 | | {Py_mod_gil, Py_MOD_GIL_NOT_USED}, |
327 | | {0, NULL} |
328 | | }; |
329 | | |
330 | | static struct PyModuleDef atexitmodule = { |
331 | | PyModuleDef_HEAD_INIT, |
332 | | .m_name = "atexit", |
333 | | .m_doc = atexit__doc__, |
334 | | .m_size = 0, |
335 | | .m_methods = atexit_methods, |
336 | | .m_slots = atexitmodule_slots, |
337 | | }; |
338 | | |
339 | | PyMODINIT_FUNC |
340 | | PyInit_atexit(void) |
341 | 0 | { |
342 | 0 | return PyModuleDef_Init(&atexitmodule); |
343 | 0 | } |