bpo-29881: Add new C API for variables init once#780
bpo-29881: Add new C API for variables init once#780vstinner wants to merge 4 commits intopython:masterfrom vstinner:staticvar_list
Conversation
|
@Haypo, thanks for your PR! By analyzing the history of the files in this pull request, we identified @loewis, @benjaminp and @larryhastings to be potential reviewers. |
Objects/object.c
Outdated
There was a problem hiding this comment.
Needed spaces around =.
And I think it would be better to clear resources in the reversed order.
Objects/object.c
Outdated
There was a problem hiding this comment.
I think the type of array should be PyObject ***.
Objects/object.c
Outdated
There was a problem hiding this comment.
The static variable should be cleared.
Objects/object.c
Outdated
There was a problem hiding this comment.
Just for the case if the destructor of freed value triggered _PyOnceVar_Set() it would be safe to save the array in the local variable and clear the once_variables structure before calling destructors.
Include/object.h
Outdated
Include/object.h
Outdated
Include/object.h
Outdated
There was a problem hiding this comment.
PyObject *var rather than PyObject* var.
Modules/arraymodule.c
Outdated
There was a problem hiding this comment.
Actually we can use PyObject_GetAttrString(). In any case this is called only once. No need to keep _array_reconstructo in a list of identifiers.
Objects/object.c
Outdated
There was a problem hiding this comment.
If set once_variables.len to PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(PyObject *) the following call of _PyOnceVar_Set() will fail. once_variables.len can be set to 0 after finalizing static variables.
There was a problem hiding this comment.
If for some reasons, a variable is initialized again: I prefer to not fail in a Python binary compiled in release mode. It's not a big deal. We already allow to import modules during Python shutdown...
There was a problem hiding this comment.
It is better to fail destructing one particular object and complain in PyErr_WriteUnraisable() than crash in assert(once_variables.array == NULL); far from the place of error.
There was a problem hiding this comment.
It must be possible to initialize again a variable if Python is reinitialized. It's exactly the test done by test_embed. I fixed your concern differently: I removed the assertion and added a second call to _PyOnceVar_Fini(). In practice, a third call might be needed... but I prefer to move step by step, and see later how to handle this corner case.
Objects/object.c
Outdated
There was a problem hiding this comment.
Py_CLEAR() is safer (for common reasons of using Py_CLEAR). Or Py_SETREF(*var, NULL) if you want to save one testing for NULL.
Objects/unicodeobject.c
Outdated
There was a problem hiding this comment.
Why not just PyUnicode_InternFromString()?
Actually _PyUnicode_FromId() can be implemented with _PY_ONCEVAR_INIT():
if (_PY_ONCEVAR_INIT(id->object, PyUnicode_InternFromString(id->string)) < 0)
return NULL;
return id->object;
Objects/object.c
Outdated
There was a problem hiding this comment.
Since calculating value is not an atomic operation (it can involve even importing) there is possible a race condition. If *var is not NULL, just decref value and return 0.
There was a problem hiding this comment.
While I agree that the case can occur in practice, I'm not confortable with supporting "reentrant calls" (reentrant calls to _PYONCEVAR_INIT() to be exact). I prefer to start with an assertion, and decide how to handle this corner case if it occurs in practice.
If reentrant calls occurs, to be clear: it would already be a bug in CPython, right? So my code doesn't make CPython worse. It's better, since the assertion now detects the bug.
New C API for variables only initialized once to be able to clear them at exit: * New macro _PY_ONCEVAR_INIT(var, expr) to initialize a variable once * New function _PyOnceVar_Set() to explicitly set a variable once to initialize it * New _PyOnceVar_Fini() function clearing all variables (initialized once) at exit
Rewrite _PyUnicode_FromId() using _PY_ONCEVAR_INIT().
| (false_str = PyUnicode_InternFromString("False")); | ||
| Py_XINCREF(s); | ||
| if (self == Py_True) { | ||
| if (_PY_ONCEVAR_INIT(true_str, PyUnicode_InternFromString("True"))) { |
There was a problem hiding this comment.
If make _PY_ONCEVAR_INIT() returning the value (NULL in case of error) the code would be simpler.
if (self == Py_True) {
s = _PY_ONCEVAR_INIT(true_str, PyUnicode_InternFromString("True"));
}
else {
s = _PY_ONCEVAR_INIT(false_str, PyUnicode_InternFromString("False"));
}
Py_XINCREF(s);
return s;
New C API for variables only initialized once to be able to clear
them at exit:
once
to initialize it
once) at exit
https://bugs.python.org/issue29881