Skip to content

bpo-29881: Add new C API for variables init once#780

Closed
vstinner wants to merge 4 commits intopython:masterfrom
vstinner:staticvar_list
Closed

bpo-29881: Add new C API for variables init once#780
vstinner wants to merge 4 commits intopython:masterfrom
vstinner:staticvar_list

Conversation

@vstinner
Copy link
Copy Markdown
Member

@vstinner vstinner commented Mar 23, 2017

New C API for variables only initialized once to be able to clear
them at exit:

  • New macro _Py_ONCEVAR(var) to declare a variable
  • 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

https://bugs.python.org/issue29881

@mention-bot
Copy link
Copy Markdown

@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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed spaces around =.

And I think it would be better to clear resources in the reversed order.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Objects/object.c Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the type of array should be PyObject ***.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right

Objects/object.c Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static variable should be cleared.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Objects/object.c Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Include/object.h Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, remove

Include/object.h Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"this this"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Include/object.h Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyObject *var rather than PyObject* var.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macro removed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Objects/object.c Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for Py_CLEAR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, done.

Objects/object.c Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"))) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

@vstinner vstinner closed this Feb 1, 2018
@vstinner vstinner deleted the staticvar_list branch May 29, 2018 22:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants