-
-
Notifications
You must be signed in to change notification settings - Fork 34.3k
gh-146302: make Py_IsInitialized() thread-safe and reflect true init completion #146303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1eda370
efeae3b
3e1eedd
59f51a2
2e3aa7e
ba28b37
abae231
01df0a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| :c:func:`Py_IsInitialized` no longer returns true until initialization has | ||
| fully completed, including import of the :mod:`site` module. The underlying | ||
| runtime flags now use atomic operations. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -166,13 +166,13 @@ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ | |
| int | ||
| _Py_IsCoreInitialized(void) | ||
| { | ||
| return _PyRuntime.core_initialized; | ||
| return _PyRuntimeState_GetCoreInitialized(&_PyRuntime); | ||
| } | ||
|
|
||
| int | ||
| Py_IsInitialized(void) | ||
| { | ||
| return _PyRuntime.initialized; | ||
| return _PyRuntimeState_GetInitialized(&_PyRuntime); | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -491,7 +491,7 @@ static PyStatus | |
| pycore_init_runtime(_PyRuntimeState *runtime, | ||
| const PyConfig *config) | ||
| { | ||
| if (runtime->initialized) { | ||
| if (_PyRuntimeState_GetInitialized(runtime)) { | ||
| return _PyStatus_ERR("main interpreter already initialized"); | ||
| } | ||
|
|
||
|
|
@@ -984,7 +984,7 @@ pyinit_config(_PyRuntimeState *runtime, | |
| } | ||
|
|
||
| /* Only when we get here is the runtime core fully initialized */ | ||
| runtime->core_initialized = 1; | ||
| _PyRuntimeState_SetCoreInitialized(runtime, 1); | ||
| return _PyStatus_OK(); | ||
| } | ||
|
|
||
|
|
@@ -1217,7 +1217,7 @@ init_interp_main(PyThreadState *tstate) | |
| * or pure Python code in the standard library won't work. | ||
| */ | ||
| if (is_main_interp) { | ||
| interp->runtime->initialized = 1; | ||
| _PyRuntimeState_SetInitialized(interp->runtime, 1); | ||
| } | ||
| return _PyStatus_OK(); | ||
| } | ||
|
|
@@ -1329,8 +1329,6 @@ init_interp_main(PyThreadState *tstate) | |
| Py_XDECREF(warnings_module); | ||
| } | ||
| Py_XDECREF(warnoptions); | ||
|
|
||
| interp->runtime->initialized = 1; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving this down after site and the rest of the machinery is the part I'm most interested in seeing tested. Intuitively I would not expect any code site may import to ever care about Py_IsInitialized() itself as extension module code "should" never have a reason to use that API as it shouldn't be called outside of an interpreter. But this is the one thing in this change that might be an observable behavior change. Even if we don't agree with someone relying on the existing behavior. |
||
| } | ||
|
|
||
| if (config->site_import) { | ||
|
|
@@ -1426,6 +1424,10 @@ init_interp_main(PyThreadState *tstate) | |
|
|
||
| assert(!_PyErr_Occurred(tstate)); | ||
|
|
||
| if (is_main_interp) { | ||
| _PyRuntimeState_SetInitialized(interp->runtime, 1); | ||
| } | ||
|
|
||
| return _PyStatus_OK(); | ||
| } | ||
|
|
||
|
|
@@ -1445,11 +1447,11 @@ static PyStatus | |
| pyinit_main(PyThreadState *tstate) | ||
| { | ||
| PyInterpreterState *interp = tstate->interp; | ||
| if (!interp->runtime->core_initialized) { | ||
| if (!_PyRuntimeState_GetCoreInitialized(interp->runtime)) { | ||
| return _PyStatus_ERR("runtime core not initialized"); | ||
| } | ||
|
|
||
| if (interp->runtime->initialized) { | ||
| if (_PyRuntimeState_GetInitialized(interp->runtime)) { | ||
| return pyinit_main_reconfigure(tstate); | ||
| } | ||
|
|
||
|
|
@@ -1503,9 +1505,8 @@ Py_InitializeEx(int install_sigs) | |
| if (_PyStatus_EXCEPTION(status)) { | ||
| Py_ExitStatusException(status); | ||
| } | ||
| _PyRuntimeState *runtime = &_PyRuntime; | ||
|
|
||
| if (runtime->initialized) { | ||
| if (Py_IsInitialized()) { | ||
| /* bpo-33932: Calling Py_Initialize() twice does nothing. */ | ||
| return; | ||
| } | ||
|
|
@@ -2210,7 +2211,7 @@ _Py_Finalize(_PyRuntimeState *runtime) | |
| int status = 0; | ||
|
|
||
| /* Bail out early if already finalized (or never initialized). */ | ||
| if (!runtime->initialized) { | ||
| if (!_PyRuntimeState_GetInitialized(runtime)) { | ||
| return status; | ||
| } | ||
|
|
||
|
|
@@ -2245,8 +2246,8 @@ _Py_Finalize(_PyRuntimeState *runtime) | |
| when they attempt to take the GIL (ex: PyEval_RestoreThread()). */ | ||
| _PyInterpreterState_SetFinalizing(tstate->interp, tstate); | ||
| _PyRuntimeState_SetFinalizing(runtime, tstate); | ||
| runtime->initialized = 0; | ||
| runtime->core_initialized = 0; | ||
| _PyRuntimeState_SetInitialized(runtime, 0); | ||
| _PyRuntimeState_SetCoreInitialized(runtime, 0); | ||
|
|
||
| // XXX Call something like _PyImport_Disable() here? | ||
|
|
||
|
|
@@ -2472,7 +2473,7 @@ new_interpreter(PyThreadState **tstate_p, | |
| } | ||
| _PyRuntimeState *runtime = &_PyRuntime; | ||
|
|
||
| if (!runtime->initialized) { | ||
| if (!_PyRuntimeState_GetInitialized(runtime)) { | ||
| return _PyStatus_ERR("Py_Initialize must be called first"); | ||
| } | ||
|
|
||
|
|
@@ -3312,10 +3313,10 @@ fatal_error_dump_runtime(int fd, _PyRuntimeState *runtime) | |
| _Py_DumpHexadecimal(fd, (uintptr_t)finalizing, sizeof(finalizing) * 2); | ||
| PUTS(fd, ")"); | ||
| } | ||
| else if (runtime->initialized) { | ||
| else if (_PyRuntimeState_GetInitialized(runtime)) { | ||
| PUTS(fd, "initialized"); | ||
| } | ||
| else if (runtime->core_initialized) { | ||
| else if (_PyRuntimeState_GetCoreInitialized(runtime)) { | ||
| PUTS(fd, "core initialized"); | ||
| } | ||
| else if (runtime->preinitialized) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is technically a bug fix, we don't always call these out in the main docs. i'm happy dropping this, but it feels worthwhile to mention for embedders likely to use this API. and if we choose to backport this as a bugfix it'll include proper 3.14.x and 3.13.x attribution to indicate when people need to work around it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is fine to add versionchanged when an important behavior needed to be pointed out in a bugfix release so I don't mind keeping this here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However considering what it changed I think it is best to keep it in 3.15 only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I'm fine not backporting this. It's rare enough and PyO3 already has a workaround.