Branch data Line data Source code
1 : :
2 : : /* Testing module for single-phase initialization of extension modules
3 : : */
4 : : #ifndef Py_BUILD_CORE_BUILTIN
5 : : # define Py_BUILD_CORE_MODULE 1
6 : : #endif
7 : :
8 : : //#include <time.h>
9 : : #include "Python.h"
10 : : #include "pycore_namespace.h" // _PyNamespace_New()
11 : :
12 : :
13 : : typedef struct {
14 : : _PyTime_t initialized;
15 : : PyObject *error;
16 : : PyObject *int_const;
17 : : PyObject *str_const;
18 : : } module_state;
19 : :
20 : :
21 : : /* Process-global state is only used by _testsinglephase
22 : : since it's the only one that does not support re-init. */
23 : : static struct {
24 : : int initialized_count;
25 : : module_state module;
26 : : } global_state = {
27 : :
28 : : #define NOT_INITIALIZED -1
29 : : .initialized_count = NOT_INITIALIZED,
30 : : };
31 : :
32 : : static void clear_state(module_state *state);
33 : :
34 : : static void
35 : 0 : clear_global_state(void)
36 : : {
37 : 0 : clear_state(&global_state.module);
38 : 0 : global_state.initialized_count = NOT_INITIALIZED;
39 : 0 : }
40 : :
41 : :
42 : : static inline module_state *
43 : 0 : get_module_state(PyObject *module)
44 : : {
45 : 0 : PyModuleDef *def = PyModule_GetDef(module);
46 [ # # ]: 0 : if (def->m_size == -1) {
47 : 0 : return &global_state.module;
48 : : }
49 [ # # ]: 0 : else if (def->m_size == 0) {
50 : 0 : return NULL;
51 : : }
52 : : else {
53 : 0 : module_state *state = (module_state*)PyModule_GetState(module);
54 : : assert(state != NULL);
55 : 0 : return state;
56 : : }
57 : : }
58 : :
59 : : static void
60 : 1 : clear_state(module_state *state)
61 : : {
62 : 1 : state->initialized = 0;
63 [ - + ]: 1 : Py_CLEAR(state->error);
64 [ - + ]: 1 : Py_CLEAR(state->int_const);
65 [ - + ]: 1 : Py_CLEAR(state->str_const);
66 : 1 : }
67 : :
68 : : static int
69 : 1 : _set_initialized(_PyTime_t *initialized)
70 : : {
71 : : /* We go strictly monotonic to ensure each time is unique. */
72 : : _PyTime_t prev;
73 [ - + ]: 1 : if (_PyTime_GetMonotonicClockWithInfo(&prev, NULL) != 0) {
74 : 0 : return -1;
75 : : }
76 : : /* We do a busy sleep since the interval should be super short. */
77 : : _PyTime_t t;
78 : : do {
79 [ - + ]: 1 : if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) != 0) {
80 : 0 : return -1;
81 : : }
82 [ - + ]: 1 : } while (t == prev);
83 : :
84 : 1 : *initialized = t;
85 : 1 : return 0;
86 : : }
87 : :
88 : : static int
89 : 1 : init_state(module_state *state)
90 : : {
91 : : assert(state->initialized == 0 &&
92 : : state->error == NULL &&
93 : : state->int_const == NULL &&
94 : : state->str_const == NULL);
95 : :
96 [ - + ]: 1 : if (_set_initialized(&state->initialized) != 0) {
97 : 0 : goto error;
98 : : }
99 : : assert(state->initialized > 0);
100 : :
101 : : /* Add an exception type */
102 : 1 : state->error = PyErr_NewException("_testsinglephase.error", NULL, NULL);
103 [ - + ]: 1 : if (state->error == NULL) {
104 : 0 : goto error;
105 : : }
106 : :
107 : 1 : state->int_const = PyLong_FromLong(1969);
108 [ - + ]: 1 : if (state->int_const == NULL) {
109 : 0 : goto error;
110 : : }
111 : :
112 : 1 : state->str_const = PyUnicode_FromString("something different");
113 [ - + ]: 1 : if (state->str_const == NULL) {
114 : 0 : goto error;
115 : : }
116 : :
117 : 1 : return 0;
118 : :
119 : 0 : error:
120 : 0 : clear_state(state);
121 : 0 : return -1;
122 : : }
123 : :
124 : :
125 : : static int
126 : 1 : init_module(PyObject *module, module_state *state)
127 : : {
128 [ - + ]: 1 : if (PyModule_AddObjectRef(module, "error", state->error) != 0) {
129 : 0 : return -1;
130 : : }
131 [ - + ]: 1 : if (PyModule_AddObjectRef(module, "int_const", state->int_const) != 0) {
132 : 0 : return -1;
133 : : }
134 [ - + ]: 1 : if (PyModule_AddObjectRef(module, "str_const", state->str_const) != 0) {
135 : 0 : return -1;
136 : : }
137 : :
138 : 1 : double d = _PyTime_AsSecondsDouble(state->initialized);
139 : 1 : PyObject *initialized = PyFloat_FromDouble(d);
140 [ - + ]: 1 : if (initialized == NULL) {
141 : 0 : return -1;
142 : : }
143 [ - + ]: 1 : if (PyModule_AddObjectRef(module, "_module_initialized", initialized) != 0) {
144 : 0 : return -1;
145 : : }
146 : :
147 : 1 : return 0;
148 : : }
149 : :
150 : :
151 : : PyDoc_STRVAR(common_state_initialized_doc,
152 : : "state_initialized()\n\
153 : : \n\
154 : : Return the seconds-since-epoch when the module state was initialized.");
155 : :
156 : : static PyObject *
157 : 0 : common_state_initialized(PyObject *self, PyObject *Py_UNUSED(ignored))
158 : : {
159 : 0 : module_state *state = get_module_state(self);
160 [ # # ]: 0 : if (state == NULL) {
161 : 0 : Py_RETURN_NONE;
162 : : }
163 : 0 : double d = _PyTime_AsSecondsDouble(state->initialized);
164 : 0 : return PyFloat_FromDouble(d);
165 : : }
166 : :
167 : : #define STATE_INITIALIZED_METHODDEF \
168 : : {"state_initialized", common_state_initialized, METH_NOARGS, \
169 : : common_state_initialized_doc}
170 : :
171 : :
172 : : PyDoc_STRVAR(common_look_up_self_doc,
173 : : "look_up_self()\n\
174 : : \n\
175 : : Return the module associated with this module's def.m_base.m_index.");
176 : :
177 : : static PyObject *
178 : 0 : common_look_up_self(PyObject *self, PyObject *Py_UNUSED(ignored))
179 : : {
180 : 0 : PyModuleDef *def = PyModule_GetDef(self);
181 [ # # ]: 0 : if (def == NULL) {
182 : 0 : return NULL;
183 : : }
184 : 0 : return Py_NewRef(
185 : : PyState_FindModule(def));
186 : : }
187 : :
188 : : #define LOOK_UP_SELF_METHODDEF \
189 : : {"look_up_self", common_look_up_self, METH_NOARGS, common_look_up_self_doc}
190 : :
191 : :
192 : : /* Function of two integers returning integer */
193 : :
194 : : PyDoc_STRVAR(common_sum_doc,
195 : : "sum(i,j)\n\
196 : : \n\
197 : : Return the sum of i and j.");
198 : :
199 : : static PyObject *
200 : 0 : common_sum(PyObject *self, PyObject *args)
201 : : {
202 : : long i, j;
203 : : long res;
204 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "ll:sum", &i, &j))
205 : 0 : return NULL;
206 : 0 : res = i + j;
207 : 0 : return PyLong_FromLong(res);
208 : : }
209 : :
210 : : #define SUM_METHODDEF \
211 : : {"sum", common_sum, METH_VARARGS, common_sum_doc}
212 : :
213 : :
214 : : PyDoc_STRVAR(basic_initialized_count_doc,
215 : : "initialized_count()\n\
216 : : \n\
217 : : Return how many times the module has been initialized.");
218 : :
219 : : static PyObject *
220 : 0 : basic_initialized_count(PyObject *self, PyObject *Py_UNUSED(ignored))
221 : : {
222 : : assert(PyModule_GetDef(self)->m_size == -1);
223 : 0 : return PyLong_FromLong(global_state.initialized_count);
224 : : }
225 : :
226 : : #define INITIALIZED_COUNT_METHODDEF \
227 : : {"initialized_count", basic_initialized_count, METH_NOARGS, \
228 : : basic_initialized_count_doc}
229 : :
230 : :
231 : : PyDoc_STRVAR(basic__clear_globals_doc,
232 : : "_clear_globals()\n\
233 : : \n\
234 : : Free all global state and set it to uninitialized.");
235 : :
236 : : static PyObject *
237 : 0 : basic__clear_globals(PyObject *self, PyObject *Py_UNUSED(ignored))
238 : : {
239 : : assert(PyModule_GetDef(self)->m_size == -1);
240 : 0 : clear_global_state();
241 : 0 : Py_RETURN_NONE;
242 : : }
243 : :
244 : : #define _CLEAR_GLOBALS_METHODDEF \
245 : : {"_clear_globals", basic__clear_globals, METH_NOARGS, \
246 : : basic__clear_globals_doc}
247 : :
248 : :
249 : : /*********************************************/
250 : : /* the _testsinglephase module (and aliases) */
251 : : /*********************************************/
252 : :
253 : : /* This ia more typical of legacy extensions in the wild:
254 : : - single-phase init
255 : : - no module state
256 : : - does not support repeated initialization
257 : : (so m_copy is used)
258 : : - the module def is cached in _PyRuntime.extensions
259 : : (by name/filename)
260 : :
261 : : Also note that, because the module has single-phase init,
262 : : it is cached in interp->module_by_index (using mod->md_def->m_base.m_index).
263 : : */
264 : :
265 : : static PyMethodDef TestMethods_Basic[] = {
266 : : LOOK_UP_SELF_METHODDEF,
267 : : SUM_METHODDEF,
268 : : STATE_INITIALIZED_METHODDEF,
269 : : INITIALIZED_COUNT_METHODDEF,
270 : : _CLEAR_GLOBALS_METHODDEF,
271 : : {NULL, NULL} /* sentinel */
272 : : };
273 : :
274 : : static struct PyModuleDef _testsinglephase_basic = {
275 : : PyModuleDef_HEAD_INIT,
276 : : .m_name = "_testsinglephase",
277 : : .m_doc = PyDoc_STR("Test module _testsinglephase"),
278 : : .m_size = -1, // no module state
279 : : .m_methods = TestMethods_Basic,
280 : : };
281 : :
282 : : static PyObject *
283 : 1 : init__testsinglephase_basic(PyModuleDef *def)
284 : : {
285 [ + - ]: 1 : if (global_state.initialized_count == -1) {
286 : 1 : global_state.initialized_count = 0;
287 : : }
288 : :
289 : 1 : PyObject *module = PyModule_Create(def);
290 [ - + ]: 1 : if (module == NULL) {
291 : 0 : return NULL;
292 : : }
293 : :
294 : 1 : module_state *state = &global_state.module;
295 : : // It may have been set by a previous run or under a different name.
296 : 1 : clear_state(state);
297 [ - + ]: 1 : if (init_state(state) < 0) {
298 [ # # ]: 0 : Py_CLEAR(module);
299 : 0 : return NULL;
300 : : }
301 : :
302 [ - + ]: 1 : if (init_module(module, state) < 0) {
303 [ # # ]: 0 : Py_CLEAR(module);
304 : 0 : goto finally;
305 : : }
306 : :
307 : 1 : global_state.initialized_count++;
308 : :
309 : 1 : finally:
310 : 1 : return module;
311 : : }
312 : :
313 : : PyMODINIT_FUNC
314 : 1 : PyInit__testsinglephase(void)
315 : : {
316 : 1 : return init__testsinglephase_basic(&_testsinglephase_basic);
317 : : }
318 : :
319 : :
320 : : PyMODINIT_FUNC
321 : 0 : PyInit__testsinglephase_basic_wrapper(void)
322 : : {
323 : 0 : return PyInit__testsinglephase();
324 : : }
325 : :
326 : :
327 : : PyMODINIT_FUNC
328 : 0 : PyInit__testsinglephase_basic_copy(void)
329 : : {
330 : : static struct PyModuleDef def = {
331 : : PyModuleDef_HEAD_INIT,
332 : : .m_name = "_testsinglephase_basic_copy",
333 : : .m_doc = PyDoc_STR("Test module _testsinglephase_basic_copy"),
334 : : .m_size = -1, // no module state
335 : : .m_methods = TestMethods_Basic,
336 : : };
337 : 0 : return init__testsinglephase_basic(&def);
338 : : }
339 : :
340 : :
341 : : /*******************************************/
342 : : /* the _testsinglephase_with_reinit module */
343 : : /*******************************************/
344 : :
345 : : /* This ia less typical of legacy extensions in the wild:
346 : : - single-phase init (same as _testsinglephase above)
347 : : - no module state
348 : : - supports repeated initialization
349 : : (so m_copy is not used)
350 : : - the module def is not cached in _PyRuntime.extensions
351 : :
352 : : At this point most modules would reach for multi-phase init (PEP 489).
353 : : However, module state has been around a while (PEP 3121),
354 : : and most extensions predate multi-phase init.
355 : :
356 : : (This module is basically the same as _testsinglephase,
357 : : but supports repeated initialization.)
358 : : */
359 : :
360 : : static PyMethodDef TestMethods_Reinit[] = {
361 : : LOOK_UP_SELF_METHODDEF,
362 : : SUM_METHODDEF,
363 : : STATE_INITIALIZED_METHODDEF,
364 : : {NULL, NULL} /* sentinel */
365 : : };
366 : :
367 : : static struct PyModuleDef _testsinglephase_with_reinit = {
368 : : PyModuleDef_HEAD_INIT,
369 : : .m_name = "_testsinglephase_with_reinit",
370 : : .m_doc = PyDoc_STR("Test module _testsinglephase_with_reinit"),
371 : : .m_size = 0,
372 : : .m_methods = TestMethods_Reinit,
373 : : };
374 : :
375 : : PyMODINIT_FUNC
376 : 0 : PyInit__testsinglephase_with_reinit(void)
377 : : {
378 : : /* We purposefully do not try PyState_FindModule() first here
379 : : since we want to check the behavior of re-loading the module. */
380 : 0 : PyObject *module = PyModule_Create(&_testsinglephase_with_reinit);
381 [ # # ]: 0 : if (module == NULL) {
382 : 0 : return NULL;
383 : : }
384 : :
385 : : assert(get_module_state(module) == NULL);
386 : :
387 : 0 : module_state state = {0};
388 [ # # ]: 0 : if (init_state(&state) < 0) {
389 [ # # ]: 0 : Py_CLEAR(module);
390 : 0 : return NULL;
391 : : }
392 : :
393 [ # # ]: 0 : if (init_module(module, &state) < 0) {
394 [ # # ]: 0 : Py_CLEAR(module);
395 : 0 : goto finally;
396 : : }
397 : :
398 : 0 : finally:
399 : : /* We only needed the module state for setting the module attrs. */
400 : 0 : clear_state(&state);
401 : 0 : return module;
402 : : }
403 : :
404 : :
405 : : /******************************************/
406 : : /* the _testsinglephase_with_state module */
407 : : /******************************************/
408 : :
409 : : /* This ia less typical of legacy extensions in the wild:
410 : : - single-phase init (same as _testsinglephase above)
411 : : - has some module state
412 : : - supports repeated initialization
413 : : (so m_copy is not used)
414 : : - the module def is not cached in _PyRuntime.extensions
415 : :
416 : : At this point most modules would reach for multi-phase init (PEP 489).
417 : : However, module state has been around a while (PEP 3121),
418 : : and most extensions predate multi-phase init.
419 : : */
420 : :
421 : : static PyMethodDef TestMethods_WithState[] = {
422 : : LOOK_UP_SELF_METHODDEF,
423 : : SUM_METHODDEF,
424 : : STATE_INITIALIZED_METHODDEF,
425 : : {NULL, NULL} /* sentinel */
426 : : };
427 : :
428 : : static struct PyModuleDef _testsinglephase_with_state = {
429 : : PyModuleDef_HEAD_INIT,
430 : : .m_name = "_testsinglephase_with_state",
431 : : .m_doc = PyDoc_STR("Test module _testsinglephase_with_state"),
432 : : .m_size = sizeof(module_state),
433 : : .m_methods = TestMethods_WithState,
434 : : };
435 : :
436 : : PyMODINIT_FUNC
437 : 0 : PyInit__testsinglephase_with_state(void)
438 : : {
439 : : /* We purposefully do not try PyState_FindModule() first here
440 : : since we want to check the behavior of re-loading the module. */
441 : 0 : PyObject *module = PyModule_Create(&_testsinglephase_with_state);
442 [ # # ]: 0 : if (module == NULL) {
443 : 0 : return NULL;
444 : : }
445 : :
446 : 0 : module_state *state = get_module_state(module);
447 : : assert(state != NULL);
448 [ # # ]: 0 : if (init_state(state) < 0) {
449 [ # # ]: 0 : Py_CLEAR(module);
450 : 0 : return NULL;
451 : : }
452 : :
453 [ # # ]: 0 : if (init_module(module, state) < 0) {
454 : 0 : clear_state(state);
455 [ # # ]: 0 : Py_CLEAR(module);
456 : 0 : goto finally;
457 : : }
458 : :
459 : 0 : finally:
460 : 0 : return module;
461 : : }
|