LCOV - code coverage report
Current view: | top level - Modules - _testsinglephase.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 44 | 128 | 34.4 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 6 | 17 | 35.3 %
|  |  | Branches: | 19 | 72 | 26.4 %

               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                 :            : }

Generated by: LCOV version 1.14