@@ -266,6 +266,23 @@ hashtable_unicode_compare(const void *key1, const void *key2) |
266 | 266 | } |
267 | 267 | } |
268 | 268 |
|
| 269 | +/* Return true if this interpreter should share the main interpreter's |
| 270 | + intern_dict. That's important for interpreters which load basic |
| 271 | + single-phase init extension modules (m_size == -1). There could be interned |
| 272 | + immortal strings that are shared between interpreters, due to the |
| 273 | + PyDict_Update(mdict, m_copy) call in import_find_extension(). |
| 274 | +
|
| 275 | + It's not safe to deallocate those strings until all interpreters that |
| 276 | + potentially use them are freed. By storing them in the main interpreter, we |
| 277 | + ensure they get freed after all other interpreters are freed. |
| 278 | +*/ |
| 279 | +static bool |
| 280 | +has_shared_intern_dict(PyInterpreterState *interp) |
| 281 | +{ |
| 282 | + PyInterpreterState *main_interp = _PyInterpreterState_Main(); |
| 283 | + return interp != main_interp && interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC; |
| 284 | +} |
| 285 | + |
269 | 286 | static int |
270 | 287 | init_interned_dict(PyInterpreterState *interp) |
271 | 288 | { |
@@ -284,9 +301,16 @@ init_interned_dict(PyInterpreterState *interp) |
284 | 301 | } |
285 | 302 | } |
286 | 303 | assert(get_interned_dict(interp) == NULL); |
287 | | - PyObject *interned = interned = PyDict_New(); |
288 | | - if (interned == NULL) { |
289 | | - return -1; |
| 304 | + PyObject *interned; |
| 305 | + if (has_shared_intern_dict(interp)) { |
| 306 | + interned = get_interned_dict(_PyInterpreterState_Main()); |
| 307 | + Py_INCREF(interned); |
| 308 | + } |
| 309 | + else { |
| 310 | + interned = PyDict_New(); |
| 311 | + if (interned == NULL) { |
| 312 | + return -1; |
| 313 | + } |
290 | 314 | } |
291 | 315 | _Py_INTERP_CACHED_OBJECT(interp, interned_strings) = interned; |
292 | 316 | return 0; |
@@ -297,7 +321,10 @@ clear_interned_dict(PyInterpreterState *interp) |
297 | 321 | { |
298 | 322 | PyObject *interned = get_interned_dict(interp); |
299 | 323 | if (interned != NULL) { |
300 | | - PyDict_Clear(interned); |
| 324 | + if (!has_shared_intern_dict(interp)) { |
| 325 | + // only clear if the dict belongs to this interpreter |
| 326 | + PyDict_Clear(interned); |
| 327 | + } |
301 | 328 | Py_DECREF(interned); |
302 | 329 | _Py_INTERP_CACHED_OBJECT(interp, interned_strings) = NULL; |
303 | 330 | } |
@@ -14861,6 +14888,13 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) |
14861 | 14888 | } |
14862 | 14889 | assert(PyDict_CheckExact(interned)); |
14863 | 14890 |
|
| 14891 | + if (has_shared_intern_dict(interp)) { |
| 14892 | + // the dict doesn't belong to this interpreter, skip the debug |
| 14893 | + // checks on it and just clear the pointer to it |
| 14894 | + clear_interned_dict(interp); |
| 14895 | + return; |
| 14896 | + } |
| 14897 | + |
14864 | 14898 | /* TODO: |
14865 | 14899 | * Currently, the runtime is not able to guarantee that it can exit without |
14866 | 14900 | * allocations that carry over to a future initialization of Python within |
@@ -15364,8 +15398,10 @@ _PyUnicode_Fini(PyInterpreterState *interp) |
15364 | 15398 | { |
15365 | 15399 | struct _Py_unicode_state *state = &interp->unicode; |
15366 | 15400 |
|
15367 | | - // _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini() |
15368 | | - assert(get_interned_dict(interp) == NULL); |
| 15401 | + if (!has_shared_intern_dict(interp)) { |
| 15402 | + // _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini() |
| 15403 | + assert(get_interned_dict(interp) == NULL); |
| 15404 | + } |
15369 | 15405 |
|
15370 | 15406 | _PyUnicode_FiniEncodings(&state->fs_codec); |
15371 | 15407 |
|
|