← 返回首页
bpo-36876: Avoid static locals. by ericsnowcurrently · Pull Request #13372 · python/cpython · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus

bpo-36876: Avoid static locals.#13372

Closed
ericsnowcurrently wants to merge 11 commits into
python:masterfrom
ericsnowcurrently:avoid-static-locals
Closed

bpo-36876: Avoid static locals.#13372
ericsnowcurrently wants to merge 11 commits into
python:masterfrom
ericsnowcurrently:avoid-static-locals

Conversation

ericsnowcurrently commented May 17, 2019
edited by bedevere-bot
Loading

Copy link
Copy Markdown
Member

This patch touches a lot of files but does only two very specific things:

  • move static locals to global scope (if they aren't truly global)
  • move all locally scoped _Py_IDENTIFIER() to global scope

Moving these to global scope makes it easier to identify problematic usage via Tools/c-globals/check-c-globals.py. It also has the side effect of dropping a number of duplicate _Py_IDENTIFIER(), thus saving a little bit of space. :)

The overall objective is to eliminate any global state that isn't truly process-global (and to ensure the remaining global state is thread-safe). This is only one of the early steps.

https://bugs.python.org/issue36876

asvetlov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

asyncio part looks good

mpd_setminalloc(mpd_ssize_t n)
{
static int minalloc_is_set = 0;
static int minalloc_is_set = 0; // Static is okay here (process-global).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Please don't change libmpdec, even if it's just a comment. :)

vstinner left a comment

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

move all locally scoped _Py_IDENTIFIER() to global scope

I dislike this change. I prefer to keep "local" static variables in functions. I don't see how moving them as globals is solving any issue, apart your practical issue of detecting them with your tool.

I would prefer to see an API to support static variable per interpreter.

I would suggest to develop an hash table similar to thread TLS: each static variable would have a global unique identifier, and its value would stored in an hash table in the PyInterpreterState. The problem is how to initialize the global unique identifier in a safe way (thread safety / atomicity). But that could be implemented using "local static" variables.

Copy link
Copy Markdown

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

Comment thread Modules/_datetimemodule.c
format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds)
{
static const char * const DayNames[] = {
static const char * const DayNames[] = { // Static is okay here (immutable data).

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Are these comments really useful? To me, it seems obvious that static constants are safe. I'm not sure of the value of such comment.

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

A comment sounds useful to flag all local statics as "good" and for a linter that checks for unverified local static vars. I'm ok with the comments, but maybe a shorter one like // static ok.?

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

I agree with @vstinner. If you really want to keep these comments, please move them to separate lines to avoid noise when using git blame.

Copy link
Copy Markdown
Member

(...) The problem is how to initialize the global unique identifier in a safe way (thread safety / atomicity). But that could be implemented using "local static" variables.

I forgot to mention that "local static variables" would be easier to initialize properly, since they cannot be accessed before the function is called: before Python is initialized.

The case of global static variables is worse: we cannot statically initialize them, we need something to dynamically initialize them. Maybe at the first access?

About the global unique identifier, one solution would be to use the memory address of the variable: it should be unique. I don't see why a compiler would merge two variables.

Draft pseudo API:

void get_interpreter_specific(uintptr_t key, size_t size, void *value); int set_interpreter_specific(uintptr_t key, size_t size, void *value); #define PyStaticVariable(TYPE) static TYPE // FIXME: add assertion to ensure that VAR is a PyStaticVariable #define PyStaticVariable_get(VAR, VALUE) \ get_interpreter_specific((uintptr_t)&(VAR), sizeof(VAR), &(VALUE)) #define PyStaticVariable_set(VAR, VALUE) \ set_interpreter_specific((uintptr_t)&(VAR), sizeof(VAR), &(VALUE)) PyObject* func(void) { PyStaticVariable(int) var; // if var doesn't exist in the interpreter local storage, // its value is always 0 by default, for any type, // as global static variables in C int value; PyStaticVariable_get(var, value); value += 1; if (PyStaticVariable_set(var, value) < 0) { return NULL; } Py_RETURN_VALUE; }

Problem: we cannot pre-allocate the storage of "var" value into the interpreter "local storage" hash table, so set_interpreter_specific() can fail with a memory allocation failure :-(

tiran commented May 17, 2019

Copy link
Copy Markdown
Member

@ericsnowcurrently Please re-generate the ast module:

Generated files not up to date M Python/Python-ast.c

skrah left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

As Victor has also requested, I'd remove all comments that mention that variables are process local.

Copy link
Copy Markdown

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

gpshead left a comment

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

overall i'm a little concerned about how fragile this is. it won't be obvious in all code why something static lives outside of the only place it is used. it feels like we need a way to prevent future C code authors from making this mistake.

Comment thread Modules/_datetimemodule.c
date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)
{
static char *keywords[] = {
static char *kwlist[] = {

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

is there a reason all of the kwlist's everywhere cannot become static const? (lemme guess.. our C API complains about the type?)

Comment thread Objects/classobject.c
return NULL;
}
return PyObject_GetAttr(im->im_func, docstr);
// if (cached_str___doc__ == NULL) {

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

checking in a block of commented out code looks wrong... what's up here?

Comment thread Python/pythonrun.c
_Py_IDENTIFIER(last_type);
_Py_IDENTIFIER(last_value);
_Py_IDENTIFIER(lineno);
_Py_IDENTIFIER(msg);

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

could the _Py_IDENTIFIER macro be modified such that it only works from the global scope so that these don't creep back into function bodies?

Copy link
Copy Markdown
Member Author

Thanks for all the feedback, everyone! I'm going to re-think this as you've all made good points. :) I may re-open the PR if it later makes sense to re-purpose it, but I'll probably instead tackle the statics in groups by Modules/Objects/Python.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

Footer

© 2026 GitHub, Inc.