From 1eda37094fcfcb7b4ecb7be122bf409a407f6091 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 11:17:50 -0700 Subject: [PATCH 1/8] gh-146302: make Py_IsInitialized() thread-safe and reflect true init completion Py_IsInitialized() previously returned 1 before Py_InitializeEx() had finished, because the runtime flag was set before site.py was imported. This caused a race in embedders like PyO3 where a second thread could observe an initialized interpreter before sys.path was fully configured. Move the initialized=1 store to the end of init_interp_main(), after site import, lazy imports, tier 2 optimizer, and dict watcher setup. Access both `initialized` and `core_initialized` through new inline accessors that use acquire/release atomics, eliminating the C-standard data race and ensuring correct visibility on weakly-ordered architectures. Fix PySys_AddAuditHook() to check core_initialized (not initialized) when deciding whether a thread state is available, since tstate exists after core init completes. Co-Authored-By: Claude Opus 4.6 (1M context) --- Doc/c-api/interp-lifecycle.rst | 5 +++ Include/internal/pycore_runtime.h | 23 +++++++++++++ Include/internal/pycore_runtime_structs.h | 12 +++++-- ...3-22-00-00-00.gh-issue-146302.PyIsInit.rst | 3 ++ Python/preconfig.c | 2 +- Python/pylifecycle.c | 34 ++++++++++--------- Python/pystate.c | 4 +-- Python/sysmodule.c | 3 +- 8 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst diff --git a/Doc/c-api/interp-lifecycle.rst b/Doc/c-api/interp-lifecycle.rst index 189d8e424f6814..186ab4370bcb9c 100644 --- a/Doc/c-api/interp-lifecycle.rst +++ b/Doc/c-api/interp-lifecycle.rst @@ -410,6 +410,11 @@ Initializing and finalizing the interpreter (zero) if not. After :c:func:`Py_FinalizeEx` is called, this returns false until :c:func:`Py_Initialize` is called again. + .. versionchanged:: next + This function no longer returns true until initialization has fully + completed, including import of the :mod:`site` module. Previously it + could return true while :c:func:`Py_Initialize` was still running. + .. c:function:: int Py_IsFinalizing() diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 7fc7f343fe600f..24c06ab2646a73 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -56,6 +56,29 @@ _PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) { } } +// Use acquire/release (not relaxed) because these are publication flags: +// when a reader sees 1, all prior initialization writes must be visible. + +static inline int +_PyRuntimeState_GetCoreInitialized(_PyRuntimeState *runtime) { + return _Py_atomic_load_int_acquire(&runtime->core_initialized); +} + +static inline void +_PyRuntimeState_SetCoreInitialized(_PyRuntimeState *runtime, int initialized) { + _Py_atomic_store_int_release(&runtime->core_initialized, initialized); +} + +static inline int +_PyRuntimeState_GetInitialized(_PyRuntimeState *runtime) { + return _Py_atomic_load_int_acquire(&runtime->initialized); +} + +static inline void +_PyRuntimeState_SetInitialized(_PyRuntimeState *runtime, int initialized) { + _Py_atomic_store_int_release(&runtime->initialized, initialized); +} + #ifdef __cplusplus } diff --git a/Include/internal/pycore_runtime_structs.h b/Include/internal/pycore_runtime_structs.h index 05369ef9f009e6..145e66de9984ca 100644 --- a/Include/internal/pycore_runtime_structs.h +++ b/Include/internal/pycore_runtime_structs.h @@ -158,10 +158,18 @@ struct pyruntimestate { /* Is Python preinitialized? Set to 1 by Py_PreInitialize() */ int preinitialized; - /* Is Python core initialized? Set to 1 by _Py_InitializeCore() */ + /* Is Python core initialized? Set to 1 by _Py_InitializeCore(). + + Use _PyRuntimeState_GetCoreInitialized() and + _PyRuntimeState_SetCoreInitialized() to access it, + don't access it directly. */ int core_initialized; - /* Is Python fully initialized? Set to 1 by Py_Initialize() */ + /* Is Python fully initialized? Set to 1 by Py_Initialize(). + + Use _PyRuntimeState_GetInitialized() and + _PyRuntimeState_SetInitialized() to access it, + don't access it directly. */ int initialized; /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize() diff --git a/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst b/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst new file mode 100644 index 00000000000000..a804427d78f2f0 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst @@ -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 with acquire/release memory ordering. diff --git a/Python/preconfig.c b/Python/preconfig.c index 0fdc0a87317712..e3c5577bc3303b 100644 --- a/Python/preconfig.c +++ b/Python/preconfig.c @@ -928,7 +928,7 @@ _PyPreConfig_Write(const PyPreConfig *src_config) return status; } - if (_PyRuntime.core_initialized) { + if (_PyRuntimeState_GetCoreInitialized(&_PyRuntime)) { /* bpo-34008: Calling this functions after Py_Initialize() ignores the new configuration. */ return _PyStatus_OK(); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 68052a91d6a1f1..6873e5098c1704 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -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; } 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); } @@ -1505,7 +1507,7 @@ Py_InitializeEx(int install_sigs) } _PyRuntimeState *runtime = &_PyRuntime; - if (runtime->initialized) { + if (_PyRuntimeState_GetInitialized(runtime)) { /* bpo-33932: Calling Py_Initialize() twice does nothing. */ return; } @@ -2210,7 +2212,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 +2247,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 +2474,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 +3314,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) { diff --git a/Python/pystate.c b/Python/pystate.c index 143175da0f45c7..3f539a4c2551ba 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -330,8 +330,8 @@ init_runtime(_PyRuntimeState *runtime, { assert(!runtime->preinitializing); assert(!runtime->preinitialized); - assert(!runtime->core_initialized); - assert(!runtime->initialized); + assert(!_PyRuntimeState_GetCoreInitialized(runtime)); + assert(!_PyRuntimeState_GetInitialized(runtime)); assert(!runtime->_initialized); runtime->open_code_hook = open_code_hook; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 646b8a1c3c3a84..d59fa2fc150559 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -33,6 +33,7 @@ Data members: #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR #include "pycore_pymem.h" // _PyMem_DefaultRawFree() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_runtime.h" // _PyRuntimeState_GetCoreInitialized() #include "pycore_pystats.h" // _Py_PrintSpecializationStats() #include "pycore_structseq.h" // _PyStructSequence_InitBuiltinWithFlags() #include "pycore_sysmodule.h" // export _PySys_GetSizeOf() @@ -471,7 +472,7 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) PySys_AddAuditHook() can be called before Python is initialized. */ _PyRuntimeState *runtime = &_PyRuntime; PyThreadState *tstate; - if (runtime->initialized) { + if (_PyRuntimeState_GetCoreInitialized(runtime)) { tstate = _PyThreadState_GET(); } else { From efeae3ba35556f36a6e58497b53ffdf5052e23a6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 11:31:16 -0700 Subject: [PATCH 2/8] Improve comment wording in pycore_runtime.h accessors Co-Authored-By: Claude Opus 4.6 (1M context) --- Include/internal/pycore_runtime.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 24c06ab2646a73..ed77a6d2b21380 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -56,8 +56,8 @@ _PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) { } } -// Use acquire/release (not relaxed) because these are publication flags: -// when a reader sees 1, all prior initialization writes must be visible. +// Release on store, acquire on load: a thread that reads initialized=1 +// is guaranteed to observe all writes from the initialization sequence. static inline int _PyRuntimeState_GetCoreInitialized(_PyRuntimeState *runtime) { From 3e1eedd470b7b2dd70fecdc6861d7027ca1eb27a Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 11:42:47 -0700 Subject: [PATCH 3/8] PySys_AddAuditHook: check initialized, not core_initialized The docs say audit hooks are notified "after runtime initialization". With the flag move, initialized=0 throughout init (including site import), so hooks are correctly not invoked during any init phase. Using core_initialized would have expanded the invocation window beyond the original behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- Python/sysmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d59fa2fc150559..1f62bb231db0dd 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -33,7 +33,7 @@ Data members: #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR #include "pycore_pymem.h" // _PyMem_DefaultRawFree() #include "pycore_pystate.h" // _PyThreadState_GET() -#include "pycore_runtime.h" // _PyRuntimeState_GetCoreInitialized() +#include "pycore_runtime.h" // _PyRuntimeState_GetInitialized() #include "pycore_pystats.h" // _Py_PrintSpecializationStats() #include "pycore_structseq.h" // _PyStructSequence_InitBuiltinWithFlags() #include "pycore_sysmodule.h" // export _PySys_GetSizeOf() @@ -472,7 +472,7 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) PySys_AddAuditHook() can be called before Python is initialized. */ _PyRuntimeState *runtime = &_PyRuntime; PyThreadState *tstate; - if (_PyRuntimeState_GetCoreInitialized(runtime)) { + if (_PyRuntimeState_GetInitialized(runtime)) { tstate = _PyThreadState_GET(); } else { From 59f51a221953a3da52aac63fa64a85bcb1a7a521 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 12:08:06 -0700 Subject: [PATCH 4/8] Add regression test: Py_IsInitialized() must be false during site import Use a C audit hook registered before Py_Initialize() to observe the value of Py_IsInitialized() when the "import" event fires for "site". With the old flag position this would have returned 1; after gh-146302 it correctly returns 0. Co-Authored-By: Claude Opus 4.6 (1M context) --- Lib/test/test_embed.py | 6 ++++++ Programs/_testembed.c | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 45d0d8308dbdea..4042f49e315a10 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1930,6 +1930,12 @@ def test_init_in_background_thread(self): out, err = self.run_embedded_interpreter("test_init_in_background_thread") self.assertEqual(err, "") + def test_isinitialized_false_during_site_import(self): + # gh-146302: Py_IsInitialized() must not return true during site import. + out, err = self.run_embedded_interpreter( + "test_isinitialized_false_during_site_import") + self.assertEqual(err, "") + class AuditingTests(EmbeddingTestsMixin, unittest.TestCase): def test_open_code_hook(self): diff --git a/Programs/_testembed.c b/Programs/_testembed.c index d4d2a7131ccb1f..40c95e4bb049e9 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -2203,6 +2203,53 @@ static int test_init_in_background_thread(void) return PyThread_join_thread(handle); } +/* gh-146302: Py_IsInitialized() must not return true during site import. */ +static int _initialized_during_site_import = -1; /* -1 = not observed */ + +static int hook_check_initialized_on_site_import( + const char *event, PyObject *args, void *userData) +{ + if (strcmp(event, "import") == 0 && args != NULL) { + PyObject *name = PyTuple_GetItem(args, 0); + if (name != NULL && PyUnicode_Check(name) + && PyUnicode_CompareWithASCIIString(name, "site") == 0 + && _initialized_during_site_import == -1) + { + _initialized_during_site_import = Py_IsInitialized(); + } + } + return 0; +} + +static int test_isinitialized_false_during_site_import(void) +{ + putenv("PYTHONMALLOC="); + _initialized_during_site_import = -1; + + /* Register audit hook before initialization */ + PySys_AddAuditHook(hook_check_initialized_on_site_import, NULL); + + _testembed_initialize(); + + if (_initialized_during_site_import == -1) { + error("audit hook never observed site import"); + Py_Finalize(); + return 1; + } + if (_initialized_during_site_import != 0) { + error("Py_IsInitialized() was true during site import"); + Py_Finalize(); + return 1; + } + if (!Py_IsInitialized()) { + error("Py_IsInitialized() was false after Py_Initialize()"); + return 1; + } + + Py_Finalize(); + return 0; +} + #ifndef MS_WINDOWS #include "test_frozenmain.h" // M_test_frozenmain @@ -2693,6 +2740,7 @@ static struct TestCase TestCases[] = { {"test_init_use_frozen_modules", test_init_use_frozen_modules}, {"test_init_main_interpreter_settings", test_init_main_interpreter_settings}, {"test_init_in_background_thread", test_init_in_background_thread}, + {"test_isinitialized_false_during_site_import", test_isinitialized_false_during_site_import}, // Audit {"test_open_code_hook", test_open_code_hook}, From 2e3aa7ea59974ace7c672464b76f7ecc3b0c038c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 12:11:37 -0700 Subject: [PATCH 5/8] Remove unnecessary putenv("PYTHONMALLOC=") from test Co-Authored-By: Claude Opus 4.6 (1M context) --- Programs/_testembed.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 40c95e4bb049e9..d09e65a9f8d0d1 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -2223,7 +2223,6 @@ static int hook_check_initialized_on_site_import( static int test_isinitialized_false_during_site_import(void) { - putenv("PYTHONMALLOC="); _initialized_during_site_import = -1; /* Register audit hook before initialization */ From ba28b37101149f95d2c49b713d7493f2ecb66836 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 12:16:44 -0700 Subject: [PATCH 6/8] Address review: use wrapper functions instead of low-level accessors Use _Py_IsCoreInitialized() in preconfig.c and Py_IsInitialized() in Py_InitializeEx(), removing the unnecessary runtime local variable. Thanks picnixz! --- Python/preconfig.c | 2 +- Python/pylifecycle.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Python/preconfig.c b/Python/preconfig.c index e3c5577bc3303b..2c8c18284c1d2d 100644 --- a/Python/preconfig.c +++ b/Python/preconfig.c @@ -928,7 +928,7 @@ _PyPreConfig_Write(const PyPreConfig *src_config) return status; } - if (_PyRuntimeState_GetCoreInitialized(&_PyRuntime)) { + if (_Py_IsCoreInitialized()) { /* bpo-34008: Calling this functions after Py_Initialize() ignores the new configuration. */ return _PyStatus_OK(); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 6873e5098c1704..09b27f99c5b113 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1505,9 +1505,8 @@ Py_InitializeEx(int install_sigs) if (_PyStatus_EXCEPTION(status)) { Py_ExitStatusException(status); } - _PyRuntimeState *runtime = &_PyRuntime; - if (_PyRuntimeState_GetInitialized(runtime)) { + if (Py_IsInitialized()) { /* bpo-33932: Calling Py_Initialize() twice does nothing. */ return; } From abae23195bfd70f1796bf8ded44f0ccb42587de0 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Mar 2026 12:36:23 -0700 Subject: [PATCH 7/8] alphabetical includes, ack that other APIs are used in the comment --- Python/sysmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 1f62bb231db0dd..217b8a9d939d6e 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -33,8 +33,8 @@ Data members: #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR #include "pycore_pymem.h" // _PyMem_DefaultRawFree() #include "pycore_pystate.h" // _PyThreadState_GET() -#include "pycore_runtime.h" // _PyRuntimeState_GetInitialized() #include "pycore_pystats.h" // _Py_PrintSpecializationStats() +#include "pycore_runtime.h" // _PyRuntimeState_Get*() #include "pycore_structseq.h" // _PyStructSequence_InitBuiltinWithFlags() #include "pycore_sysmodule.h" // export _PySys_GetSizeOf() #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal() From 01df0a2eced120b701a228e36ff3dbf611d45de6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 23 Mar 2026 05:11:48 +0000 Subject: [PATCH 8/8] Address review: use seq-cst atomics for initialized flags Not performance critical, so use the default ordering rather than acquire/release. --- Include/internal/pycore_runtime.h | 12 ++++++------ .../2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index ed77a6d2b21380..fcd2ae9b1d1f1a 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -56,27 +56,27 @@ _PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) { } } -// Release on store, acquire on load: a thread that reads initialized=1 -// is guaranteed to observe all writes from the initialization sequence. +// Atomic so a thread that reads initialized=1 observes all writes +// from the initialization sequence (gh-146302). static inline int _PyRuntimeState_GetCoreInitialized(_PyRuntimeState *runtime) { - return _Py_atomic_load_int_acquire(&runtime->core_initialized); + return _Py_atomic_load_int(&runtime->core_initialized); } static inline void _PyRuntimeState_SetCoreInitialized(_PyRuntimeState *runtime, int initialized) { - _Py_atomic_store_int_release(&runtime->core_initialized, initialized); + _Py_atomic_store_int(&runtime->core_initialized, initialized); } static inline int _PyRuntimeState_GetInitialized(_PyRuntimeState *runtime) { - return _Py_atomic_load_int_acquire(&runtime->initialized); + return _Py_atomic_load_int(&runtime->initialized); } static inline void _PyRuntimeState_SetInitialized(_PyRuntimeState *runtime, int initialized) { - _Py_atomic_store_int_release(&runtime->initialized, initialized); + _Py_atomic_store_int(&runtime->initialized, initialized); } diff --git a/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst b/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst index a804427d78f2f0..e194e2bb2c378b 100644 --- a/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst +++ b/Misc/NEWS.d/next/C_API/2026-03-22-00-00-00.gh-issue-146302.PyIsInit.rst @@ -1,3 +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 with acquire/release memory ordering. +runtime flags now use atomic operations.