[3.12] gh-105699: Use a Thread-Local Variable for PKGCONTEXT (gh-105740) (gh-105765)

This fixes a race during import. The existing _PyRuntimeState.imports.pkgcontext is shared between interpreters, and occasionally this would cause a crash when multiple interpreters were importing extensions modules at the same time.  To solve this we add a thread-local variable for the value.  We also leave the existing state (and infrequent race) in place for platforms that do not support thread-local variables.
(cherry picked from commit b87d288275)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
Miss Islington (bot) 2023-06-13 18:34:26 -07:00 committed by GitHub
parent 26bc2cc061
commit bc997b38b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 1 deletions

View file

@ -703,10 +703,19 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
_PyRuntime.imports.pkgcontext, and PyModule_Create*() will _PyRuntime.imports.pkgcontext, and PyModule_Create*() will
substitute this (if the name actually matches). substitute this (if the name actually matches).
*/ */
#ifdef HAVE_THREAD_LOCAL
_Py_thread_local const char *pkgcontext = NULL;
# undef PKGCONTEXT
# define PKGCONTEXT pkgcontext
#endif
const char * const char *
_PyImport_ResolveNameWithPackageContext(const char *name) _PyImport_ResolveNameWithPackageContext(const char *name)
{ {
#ifndef HAVE_THREAD_LOCAL
PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK); PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK);
#endif
if (PKGCONTEXT != NULL) { if (PKGCONTEXT != NULL) {
const char *p = strrchr(PKGCONTEXT, '.'); const char *p = strrchr(PKGCONTEXT, '.');
if (p != NULL && strcmp(name, p+1) == 0) { if (p != NULL && strcmp(name, p+1) == 0) {
@ -714,17 +723,23 @@ _PyImport_ResolveNameWithPackageContext(const char *name)
PKGCONTEXT = NULL; PKGCONTEXT = NULL;
} }
} }
#ifndef HAVE_THREAD_LOCAL
PyThread_release_lock(EXTENSIONS.mutex); PyThread_release_lock(EXTENSIONS.mutex);
#endif
return name; return name;
} }
const char * const char *
_PyImport_SwapPackageContext(const char *newcontext) _PyImport_SwapPackageContext(const char *newcontext)
{ {
#ifndef HAVE_THREAD_LOCAL
PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK); PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK);
#endif
const char *oldcontext = PKGCONTEXT; const char *oldcontext = PKGCONTEXT;
PKGCONTEXT = newcontext; PKGCONTEXT = newcontext;
#ifndef HAVE_THREAD_LOCAL
PyThread_release_lock(EXTENSIONS.mutex); PyThread_release_lock(EXTENSIONS.mutex);
#endif
return oldcontext; return oldcontext;
} }

View file

@ -58,6 +58,7 @@ _KEYWORD = textwrap.dedent(r'''
extern | extern |
register | register |
static | static |
_Thread_local |
typedef | typedef |
const | const |
@ -137,7 +138,7 @@ COMPOUND_TYPE_KIND = r'(?: \b (?: struct | union | enum ) \b )'
####################################### #######################################
# variable declarations # variable declarations
_STORAGE = 'auto register static extern'.split() _STORAGE = 'auto register static extern _Thread_local'.split()
STORAGE_CLASS = rf'(?: \b (?: {" | ".join(_STORAGE)} ) \b )' STORAGE_CLASS = rf'(?: \b (?: {" | ".join(_STORAGE)} ) \b )'
TYPE_QUALIFIER = r'(?: \b (?: const | volatile ) \b )' TYPE_QUALIFIER = r'(?: \b (?: const | volatile ) \b )'
PTR_QUALIFIER = rf'(?: [*] (?: \s* {TYPE_QUALIFIER} )? )' PTR_QUALIFIER = rf'(?: [*] (?: \s* {TYPE_QUALIFIER} )? )'

View file

@ -219,6 +219,7 @@ def _strip_directives(line, partial=0):
line = line[m.end():] line = line[m.end():]
line = re.sub(r'__extension__', '', line) line = re.sub(r'__extension__', '', line)
line = re.sub(r'__thread\b', '_Thread_local', line)
while (m := COMPILER_DIRECTIVE_RE.match(line)): while (m := COMPILER_DIRECTIVE_RE.match(line)):
before, _, _, closed = m.groups() before, _, _, closed = m.groups()

View file

@ -168,6 +168,12 @@ Modules/_xxinterpchannelsmodule.c - _globals -
Python/pyfpe.c - PyFPE_counter - Python/pyfpe.c - PyFPE_counter -
##-----------------------
## thread-local variables
Python/import.c - pkgcontext -
Python/pystate.c - _Py_tss_tstate -
##----------------------- ##-----------------------
## should be const ## should be const
# XXX Make them const. # XXX Make them const.

Can't render this file because it has a wrong number of fields in line 4.