/home/mdboom/Work/builds/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_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* |
18 | get_atexit_state(void) |
19 | { |
20 | PyInterpreterState *interp = _PyInterpreterState_GET(); |
21 | return &interp->atexit; |
22 | } |
23 | |
24 | |
25 | static void |
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 |
40 | atexit_cleanup(struct atexit_state *state) |
41 | { |
42 | atexit_callback *cb; |
43 | for (int i = 0; i < state->ncallbacks; i++11 ) { Branch (43:21): [True: 11, False: 277]
|
44 | cb = state->callbacks[i]; |
45 | if (cb == NULL) Branch (45:13): [True: 1, False: 10]
|
46 | continue; |
47 | |
48 | atexit_delete_cb(state, i); |
49 | } |
50 | state->ncallbacks = 0; |
51 | } |
52 | |
53 | |
54 | PyStatus |
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) { Branch (64:9): [True: 0, False: 278]
|
65 | return _PyStatus_NO_MEMORY(); |
66 | } |
67 | return _PyStatus_OK(); |
68 | } |
69 | |
70 | |
71 | void |
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 |
82 | atexit_callfuncs(struct atexit_state *state) |
83 | { |
84 | assert(!PyErr_Occurred()); |
85 | |
86 | if (state->ncallbacks == 0) { Branch (86:9): [True: 267, False: 5]
|
87 | return; |
88 | } |
89 | |
90 | for (int i = state->ncallbacks - 1; 5 i >= 0; i--11 ) { Branch (90:41): [True: 11, False: 5]
|
91 | atexit_callback *cb = state->callbacks[i]; |
92 | if (cb == NULL) { Branch (92:13): [True: 1, False: 10]
|
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) { Branch (99:13): [True: 0, False: 10]
|
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 |
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 * |
138 | atexit_register(PyObject *module, PyObject *args, PyObject *kwargs) |
139 | { |
140 | if (PyTuple_GET_SIZE(args) == 0) { Branch (140:9): [True: 0, False: 13]
|
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)) { Branch (147:9): [True: 0, False: 13]
|
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) { Branch (154:9): [True: 0, False: 13]
|
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) { Branch (159:13): [True: 0, False: 0]
|
160 | return PyErr_NoMemory(); |
161 | } |
162 | state->callbacks = r; |
163 | } |
164 | |
165 | atexit_callback *callback = PyMem_Malloc(sizeof(atexit_callback)); |
166 | if (callback == NULL) { Branch (166:9): [True: 0, False: 13]
|
167 | return PyErr_NoMemory(); |
168 | } |
169 | |
170 | callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); |
171 | if (callback->args == NULL) { Branch (171:9): [True: 0, False: 13]
|
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 * |
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 * |
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 * |
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 * |
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++5 ) Branch (234:21): [True: 5, False: 1]
|
235 | { |
236 | atexit_callback *cb = state->callbacks[i]; |
237 | if (cb == NULL) { Branch (237:13): [True: 0, False: 5]
|
238 | continue; |
239 | } |
240 | |
241 | int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ); |
242 | if (eq < 0) { Branch (242:13): [True: 0, False: 5]
|
243 | return NULL; |
244 | } |
245 | if (eq) { Branch (245:13): [True: 1, False: 4]
|
246 | atexit_delete_cb(state, i); |
247 | } |
248 | } |
249 | Py_RETURN_NONE; |
250 | } |
251 | |
252 | |
253 | static PyMethodDef atexit_methods[] = { |
254 | {"register", _PyCFunction_CAST(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 |
287 | PyInit_atexit(void) |
288 | { |
289 | return PyModuleDef_Init(&atexitmodule); |
290 | } |