Skip to content

Commit

Permalink
pythongh-105699: Fix an Interned Strings Crasher (pythongh-106930)
Browse files Browse the repository at this point in the history
A static (process-global) str object must only have its "interned" state cleared when no longer interned in any interpreters.  They are the only ones that can be shared by interpreters so we don't have to worry about any other str objects.

We trigger clearing the state with the main interpreter, since no other interpreters may exist at that point and _PyUnicode_ClearInterned() is only called during interpreter finalization.

We do not address here the fact that a string will only be interned in the first interpreter that interns it.  In any subsequent interpreters str.state.interned is already set so _PyUnicode_InternInPlace() will skip it.  That needs to be addressed separately from fixing the crasher.
  • Loading branch information
ericsnowcurrently authored Jul 21, 2023
1 parent fd84ac0 commit 87e7cb0
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 12 additions & 1 deletion Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -14818,6 +14818,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
Expand All @@ -14829,6 +14830,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 */
Expand All @@ -14837,7 +14846,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,
Expand Down

0 comments on commit 87e7cb0

Please sign in to comment.