diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-20-15-15-57.gh-issue-105699.DdqHFg.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-20-15-15-57.gh-issue-105699.DdqHFg.rst new file mode 100644 index 00000000000000..4a257c6282220f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-20-15-15-57.gh-issue-105699.DdqHFg.rst @@ -0,0 +1,3 @@ +Python no longer crashes due an infrequent race when initialzing +per-interpreter interned strings. The crash would manifest when the +interpreter was finalized. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index cd196f5b11cd9a..7ead65bf98d1f1 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14817,6 +14817,7 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) PyObject *s, *ignored_value; while (PyDict_Next(interned, &pos, &s, &ignored_value)) { assert(PyUnicode_IS_READY(s)); + int shared = 0; switch (PyUnicode_CHECK_INTERNED(s)) { case SSTATE_INTERNED_IMMORTAL: // Skip the Immortal Instance check and restore @@ -14828,6 +14829,14 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) #endif break; case SSTATE_INTERNED_IMMORTAL_STATIC: + /* It is shared between interpreters, so we should unmark it + only when this is the last interpreter in which it's + interned. We immortalize all the statically initialized + strings during startup, so we can rely on the + main interpreter to be the last one. */ + if (!_Py_IsMainInterpreter(interp)) { + shared = 1; + } break; case SSTATE_INTERNED_MORTAL: /* fall through */ @@ -14836,7 +14845,9 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) default: Py_UNREACHABLE(); } - _PyUnicode_STATE(s).interned = SSTATE_NOT_INTERNED; + if (!shared) { + _PyUnicode_STATE(s).interned = SSTATE_NOT_INTERNED; + } } #ifdef INTERNED_STATS fprintf(stderr,