Skip to content

Commit

Permalink
pythongh-105699: Use a Thread-Local Variable for PKGCONTEXT (pythongh…
Browse files Browse the repository at this point in the history
…-105740)

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 b87d288)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
  • Loading branch information
ericsnowcurrently authored and miss-islington committed Jun 14, 2023
1 parent 33d3069 commit bfb6863
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 1 deletion.
15 changes: 15 additions & 0 deletions Python/import.c
Expand Up @@ -703,28 +703,43 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
_PyRuntime.imports.pkgcontext, and PyModule_Create*() will
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 *
_PyImport_ResolveNameWithPackageContext(const char *name)
{
#ifndef HAVE_THREAD_LOCAL
PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK);
#endif
if (PKGCONTEXT != NULL) {
const char *p = strrchr(PKGCONTEXT, '.');
if (p != NULL && strcmp(name, p+1) == 0) {
name = PKGCONTEXT;
PKGCONTEXT = NULL;
}
}
#ifndef HAVE_THREAD_LOCAL
PyThread_release_lock(EXTENSIONS.mutex);
#endif
return name;
}

const char *
_PyImport_SwapPackageContext(const char *newcontext)
{
#ifndef HAVE_THREAD_LOCAL
PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK);
#endif
const char *oldcontext = PKGCONTEXT;
PKGCONTEXT = newcontext;
#ifndef HAVE_THREAD_LOCAL
PyThread_release_lock(EXTENSIONS.mutex);
#endif
return oldcontext;
}

Expand Down
3 changes: 2 additions & 1 deletion Tools/c-analyzer/c_parser/parser/_regexes.py
Expand Up @@ -58,6 +58,7 @@ def _ind(text, level=1, edges='both'):
extern |
register |
static |
_Thread_local |
typedef |
const |
Expand Down Expand Up @@ -137,7 +138,7 @@ def _ind(text, level=1, edges='both'):
#######################################
# variable declarations

_STORAGE = 'auto register static extern'.split()
_STORAGE = 'auto register static extern _Thread_local'.split()
STORAGE_CLASS = rf'(?: \b (?: {" | ".join(_STORAGE)} ) \b )'
TYPE_QUALIFIER = r'(?: \b (?: const | volatile ) \b )'
PTR_QUALIFIER = rf'(?: [*] (?: \s* {TYPE_QUALIFIER} )? )'
Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/c_parser/preprocessor/gcc.py
Expand Up @@ -219,6 +219,7 @@ def _strip_directives(line, partial=0):
line = line[m.end():]

line = re.sub(r'__extension__', '', line)
line = re.sub(r'__thread\b', '_Thread_local', line)

while (m := COMPILER_DIRECTIVE_RE.match(line)):
before, _, _, closed = m.groups()
Expand Down
6 changes: 6 additions & 0 deletions Tools/c-analyzer/cpython/ignored.tsv
Expand Up @@ -168,6 +168,12 @@ Modules/_xxinterpchannelsmodule.c - _globals -

Python/pyfpe.c - PyFPE_counter -

##-----------------------
## thread-local variables

Python/import.c - pkgcontext -
Python/pystate.c - _Py_tss_tstate -

##-----------------------
## should be const
# XXX Make them const.
Expand Down

0 comments on commit bfb6863

Please sign in to comment.