From 4cddfe55adef2b2797ee9954fa3e4409205ea13d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 22 Mar 2017 17:09:44 +0100 Subject: [PATCH] bpo-29881: Add new C API for static variables * New type: _Py_StaticVar * New macro _Py_STATICVAR(var) to declare a variable * New macro _PY_STATICVAR_INIT(var, expr) to initialize a variable once * New function _PyStaticVar_Set() to explicitly set a variable once to initialize it * New _PyStaticVar_Fini() function clearing all references at exit --- Include/object.h | 20 +++++++++++++++++++ Modules/arraymodule.c | 13 ++++++------- Objects/object.c | 33 +++++++++++++++++++++++++++++++ Objects/typeobject.c | 13 ++++++------- Python/compile.c | 45 ++++++++++++++++++++----------------------- Python/import.c | 38 ++++++++++++++++++------------------ Python/pylifecycle.c | 2 ++ Python/sysmodule.c | 10 ++++------ 8 files changed, 111 insertions(+), 63 deletions(-) diff --git a/Include/object.h b/Include/object.h index 63e37b8d33a68c..41781c1684a8a0 100644 --- a/Include/object.h +++ b/Include/object.h @@ -1071,6 +1071,26 @@ PyAPI_FUNC(void) _PyObject_DebugTypeStats(FILE *out); #endif /* ifndef Py_LIMITED_API */ +#ifndef Py_LIMITED_API +typedef struct _Py_StaticVar { + struct _Py_StaticVar *next; + PyObject *obj; +} _Py_StaticVar; + + +PyAPI_FUNC(int) _PyStaticVar_Set(_Py_StaticVar *var, PyObject *obj); + +#define _Py_STATICVAR(var) \ + static _Py_StaticVar var = {.next = NULL, .obj = NULL} + +/* Initialized the static variable 'var' once with the expression 'expr'. + Return 0 on success, return -1 on failure. */ +#define _PY_STATICVAR_INIT(var, expr) \ + ((var)->obj == NULL ? _PyStaticVar_Set((var), (expr)) : 0) + +PyAPI_DATA(_Py_StaticVar*) _Py_static_variables; +#endif + #ifdef __cplusplus } #endif diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index b30f7594c25a24..b110535ee01df0 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2124,20 +2124,19 @@ array_array___reduce_ex__(arrayobject *self, PyObject *value) PyObject *array_str; int typecode = self->ob_descr->typecode; int mformat_code; - static PyObject *array_reconstructor = NULL; + _Py_STATICVAR(array_reconstructor); long protocol; _Py_IDENTIFIER(_array_reconstructor); _Py_IDENTIFIER(__dict__); - if (array_reconstructor == NULL) { + if (array_reconstructor.obj == NULL) { PyObject *array_module = PyImport_ImportModule("array"); if (array_module == NULL) return NULL; - array_reconstructor = _PyObject_GetAttrId( - array_module, - &PyId__array_reconstructor); + PyObject *meth = _PyObject_GetAttrId(array_module, + &PyId__array_reconstructor); Py_DECREF(array_module); - if (array_reconstructor == NULL) + if (_PyStaticVar_Set(&array_reconstructor, meth)) return NULL; } @@ -2191,7 +2190,7 @@ array_array___reduce_ex__(arrayobject *self, PyObject *value) return NULL; } result = Py_BuildValue( - "O(OCiN)O", array_reconstructor, Py_TYPE(self), typecode, + "O(OCiN)O", array_reconstructor.obj, Py_TYPE(self), typecode, mformat_code, array_str, dict); Py_DECREF(dict); return result; diff --git a/Objects/object.c b/Objects/object.c index 40061b1b3c175d..87868a14ed697d 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2129,3 +2129,36 @@ _Py_Dealloc(PyObject *op) #ifdef __cplusplus } #endif + + +_Py_StaticVar* _Py_static_variables = NULL; + +int +_PyStaticVar_Set(_Py_StaticVar *var, PyObject *obj) +{ + assert(var->next == NULL); + assert(var->obj == NULL); + + if (obj == NULL) { + return -1; + } + + var->obj = obj; + var->next = _Py_static_variables; + _Py_static_variables = var; + return 0; +} + +void +_PyStaticVar_Fini(void) +{ + _Py_StaticVar *next, *var; + + for (var = _Py_static_variables; var != NULL; var = next) { + next = var->next; + + Py_CLEAR(var->obj); + var->next = NULL; + } + _Py_static_variables = NULL; +} diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 64a72d2f146612..4375e40b6182fe 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4419,15 +4419,14 @@ static PyObject * object___reduce_ex___impl(PyObject *self, int protocol) /*[clinic end generated code: output=2e157766f6b50094 input=8dd6a9602a12749e]*/ { - static PyObject *objreduce; + _Py_STATICVAR(objreduce); PyObject *reduce, *res; _Py_IDENTIFIER(__reduce__); - if (objreduce == NULL) { - objreduce = _PyDict_GetItemId(PyBaseObject_Type.tp_dict, - &PyId___reduce__); - if (objreduce == NULL) - return NULL; + if (_PY_STATICVAR_INIT(&objreduce, + _PyDict_GetItemId(PyBaseObject_Type.tp_dict, + &PyId___reduce__))) { + return NULL; } reduce = _PyObject_GetAttrId(self, &PyId___reduce__); @@ -4443,7 +4442,7 @@ object___reduce_ex___impl(PyObject *self, int protocol) Py_DECREF(reduce); return NULL; } - override = (clsreduce != objreduce); + override = (clsreduce != objreduce.obj); Py_DECREF(clsreduce); if (override) { res = _PyObject_CallNoArg(reduce); diff --git a/Python/compile.c b/Python/compile.c index 064364ea28cb11..ce927335ab2410 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -213,7 +213,7 @@ static int compiler_async_comprehension_generator( expr_ty elt, expr_ty val, int type); static PyCodeObject *assemble(struct compiler *, int addNone); -static PyObject *__doc__; +_Py_STATICVAR(__doc__); #define CAPSULE_NAME "compile.c compiler unit" @@ -306,10 +306,8 @@ PyAST_CompileObject(mod_ty mod, PyObject *filename, PyCompilerFlags *flags, PyCompilerFlags local_flags; int merged; - if (!__doc__) { - __doc__ = PyUnicode_InternFromString("__doc__"); - if (!__doc__) - return NULL; + if (_PY_STATICVAR_INIT(&__doc__, PyUnicode_InternFromString("__doc__"))) { + return 0; } if (!compiler_init(&c)) @@ -1441,7 +1439,7 @@ compiler_body(struct compiler *c, asdl_seq *stmts, string docstring) /* if not -OO mode, set docstring */ if (c->c_optimize < 2 && docstring) { ADDOP_O(c, LOAD_CONST, docstring, consts); - ADDOP_NAME(c, STORE_NAME, __doc__, names); + ADDOP_NAME(c, STORE_NAME, __doc__.obj, names); } VISIT_SEQ(c, stmt, stmts); return 1; @@ -1452,14 +1450,14 @@ compiler_mod(struct compiler *c, mod_ty mod) { PyCodeObject *co; int addNone = 1; - static PyObject *module; - if (!module) { - module = PyUnicode_InternFromString(""); - if (!module) - return NULL; + _Py_STATICVAR(module); + + if (_PY_STATICVAR_INIT(&module, PyUnicode_InternFromString(""))) { + return 0; } + /* Use 0 for firstlineno initially, will fixup in assemble(). */ - if (!compiler_enter_scope(c, module, COMPILER_SCOPE_MODULE, mod, 0)) + if (!compiler_enter_scope(c, module.obj, COMPILER_SCOPE_MODULE, mod, 0)) return NULL; switch (mod->kind) { case Module_kind: @@ -2639,12 +2637,10 @@ compiler_from_import(struct compiler *c, stmt_ty s) PyObject *names = PyTuple_New(n); PyObject *level; - static PyObject *empty_string; + _Py_STATICVAR(empty_string); - if (!empty_string) { - empty_string = PyUnicode_FromString(""); - if (!empty_string) - return 0; + if (_PY_STATICVAR_INIT(&empty_string, PyUnicode_FromString(""))) { + return 0; } if (!names) @@ -2679,7 +2675,7 @@ compiler_from_import(struct compiler *c, stmt_ty s) ADDOP_NAME(c, IMPORT_NAME, s->v.ImportFrom.module, names); } else { - ADDOP_NAME(c, IMPORT_NAME, empty_string, names); + ADDOP_NAME(c, IMPORT_NAME, empty_string.obj, names); } for (i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); @@ -2709,17 +2705,18 @@ compiler_from_import(struct compiler *c, stmt_ty s) static int compiler_assert(struct compiler *c, stmt_ty s) { - static PyObject *assertion_error = NULL; + _Py_STATICVAR(assertion_error); basicblock *end; PyObject* msg; if (c->c_optimize) return 1; - if (assertion_error == NULL) { - assertion_error = PyUnicode_InternFromString("AssertionError"); - if (assertion_error == NULL) - return 0; + + if (_PY_STATICVAR_INIT(&assertion_error, + PyUnicode_InternFromString("AssertionError"))) { + return 0; } + if (s->v.Assert.test->kind == Tuple_kind && asdl_seq_LEN(s->v.Assert.test->v.Tuple.elts) > 0) { msg = PyUnicode_FromString("assertion is always true, " @@ -2739,7 +2736,7 @@ compiler_assert(struct compiler *c, stmt_ty s) if (end == NULL) return 0; ADDOP_JABS(c, POP_JUMP_IF_TRUE, end); - ADDOP_O(c, LOAD_GLOBAL, assertion_error, names); + ADDOP_O(c, LOAD_GLOBAL, assertion_error.obj, names); if (s->v.Assert.msg) { VISIT(c, expr, s->v.Assert.msg); ADDOP_I(c, CALL_FUNCTION, 1); diff --git a/Python/import.c b/Python/import.c index 0fc4d1da547459..9a8abcfeb7ea39 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1721,9 +1721,9 @@ PyImport_ReloadModule(PyObject *m) PyObject * PyImport_Import(PyObject *module_name) { - static PyObject *silly_list = NULL; - static PyObject *builtins_str = NULL; - static PyObject *import_str = NULL; + _Py_STATICVAR(empty_list); + _Py_STATICVAR(builtins_str); + _Py_STATICVAR(import_str); PyObject *globals = NULL; PyObject *import = NULL; PyObject *builtins = NULL; @@ -1731,23 +1731,23 @@ PyImport_Import(PyObject *module_name) PyObject *r = NULL; /* Initialize constant string objects */ - if (silly_list == NULL) { - import_str = PyUnicode_InternFromString("__import__"); - if (import_str == NULL) - return NULL; - builtins_str = PyUnicode_InternFromString("__builtins__"); - if (builtins_str == NULL) - return NULL; - silly_list = PyList_New(0); - if (silly_list == NULL) - return NULL; + if (_PY_STATICVAR_INIT(&empty_list, PyList_New(0))) { + return NULL; + } + if (_PY_STATICVAR_INIT(&builtins_str, + PyUnicode_InternFromString("__builtins__"))) { + return NULL; + } + if (_PY_STATICVAR_INIT(&import_str, + PyUnicode_InternFromString("__import__"))) { + return NULL; } /* Get the builtins from current globals */ globals = PyEval_GetGlobals(); if (globals != NULL) { Py_INCREF(globals); - builtins = PyObject_GetItem(globals, builtins_str); + builtins = PyObject_GetItem(globals, builtins_str.obj); if (builtins == NULL) goto err; } @@ -1757,19 +1757,19 @@ PyImport_Import(PyObject *module_name) NULL, NULL, NULL, 0); if (builtins == NULL) return NULL; - globals = Py_BuildValue("{OO}", builtins_str, builtins); + globals = Py_BuildValue("{OO}", builtins_str.obj, builtins); if (globals == NULL) goto err; } /* Get the __import__ function from the builtins */ if (PyDict_Check(builtins)) { - import = PyObject_GetItem(builtins, import_str); + import = PyObject_GetItem(builtins, import_str.obj); if (import == NULL) - PyErr_SetObject(PyExc_KeyError, import_str); + PyErr_SetObject(PyExc_KeyError, import_str.obj); } else - import = PyObject_GetAttr(builtins, import_str); + import = PyObject_GetAttr(builtins, import_str.obj); if (import == NULL) goto err; @@ -1777,7 +1777,7 @@ PyImport_Import(PyObject *module_name) Always use absolute import here. Calling for side-effect of import. */ r = PyObject_CallFunction(import, "OOOOi", module_name, globals, - globals, silly_list, 0, NULL); + globals, empty_list.obj, 0, NULL); if (r == NULL) goto err; Py_DECREF(r); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index afd4eb8f55d7af..c640a1734fbf9f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -62,6 +62,7 @@ static void call_ll_exitfuncs(void); extern int _PyUnicode_Init(void); extern int _PyStructSequence_Init(void); extern void _PyUnicode_Fini(void); +extern void _PyStaticVar_Fini(void); extern int _PyLong_Init(void); extern void PyLong_Fini(void); extern int _PyFaulthandler_Init(void); @@ -699,6 +700,7 @@ Py_FinalizeEx(void) /* Cleanup Unicode implementation */ _PyUnicode_Fini(); + _PyStaticVar_Fini(); /* reset file system default encoding */ if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 2da77025b01497..a2281eaaa7a09e 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -162,7 +162,7 @@ sys_displayhook(PyObject *self, PyObject *o) PyInterpreterState *interp = PyThreadState_GET()->interp; PyObject *modules = interp->modules; PyObject *builtins; - static PyObject *newline = NULL; + _Py_STATICVAR(newline); int err; builtins = _PyDict_GetItemId(modules, &PyId_builtins); @@ -197,12 +197,10 @@ sys_displayhook(PyObject *self, PyObject *o) return NULL; } } - if (newline == NULL) { - newline = PyUnicode_FromString("\n"); - if (newline == NULL) - return NULL; + if (_PY_STATICVAR_INIT(&newline, PyUnicode_FromString("\n"))) { + return NULL; } - if (PyFile_WriteObject(newline, outf, Py_PRINT_RAW) != 0) + if (PyFile_WriteObject(newline.obj, outf, Py_PRINT_RAW) != 0) return NULL; if (_PyObject_SetAttrId(builtins, &PyId__, o) != 0) return NULL;