Skip to content

Commit bc997b3

Browse files
[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 b87d288) Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
1 parent 26bc2cc commit bc997b3

4 files changed

Lines changed: 24 additions & 1 deletion

File tree

‎Python/import.c‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,28 +703,43 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
703703
_PyRuntime.imports.pkgcontext, and PyModule_Create*() will
704704
substitute this (if the name actually matches).
705705
*/
706+
707+
#ifdefHAVE_THREAD_LOCAL
708+
_Py_thread_localconstchar*pkgcontext=NULL;
709+
# undef PKGCONTEXT
710+
# definePKGCONTEXT pkgcontext
711+
#endif
712+
706713
constchar*
707714
_PyImport_ResolveNameWithPackageContext(constchar*name)
708715
{
716+
#ifndefHAVE_THREAD_LOCAL
709717
PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK);
718+
#endif
710719
if (PKGCONTEXT!=NULL) {
711720
constchar*p=strrchr(PKGCONTEXT, '.');
712721
if (p!=NULL&&strcmp(name, p+1) ==0) {
713722
name=PKGCONTEXT;
714723
PKGCONTEXT=NULL;
715724
}
716725
}
726+
#ifndefHAVE_THREAD_LOCAL
717727
PyThread_release_lock(EXTENSIONS.mutex);
728+
#endif
718729
returnname;
719730
}
720731

721732
constchar*
722733
_PyImport_SwapPackageContext(constchar*newcontext)
723734
{
735+
#ifndefHAVE_THREAD_LOCAL
724736
PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK);
737+
#endif
725738
constchar*oldcontext=PKGCONTEXT;
726739
PKGCONTEXT=newcontext;
740+
#ifndefHAVE_THREAD_LOCAL
727741
PyThread_release_lock(EXTENSIONS.mutex);
742+
#endif
728743
returnoldcontext;
729744
}
730745

‎Tools/c-analyzer/c_parser/parser/_regexes.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def _ind(text, level=1, edges='both'):
5858
extern |
5959
register |
6060
static |
61+
_Thread_local |
6162
typedef |
6263
6364
const |
@@ -137,7 +138,7 @@ def _ind(text, level=1, edges='both'):
137138
#######################################
138139
# variable declarations
139140

140-
_STORAGE='auto register static extern'.split()
141+
_STORAGE='auto register static extern _Thread_local'.split()
141142
STORAGE_CLASS=rf'(?: \b (?: {" | ".join(_STORAGE)} ) \b )'
142143
TYPE_QUALIFIER=r'(?: \b (?: const | volatile ) \b )'
143144
PTR_QUALIFIER=rf'(?: [*] (?: \s* {TYPE_QUALIFIER} )? )'

‎Tools/c-analyzer/c_parser/preprocessor/gcc.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ def _strip_directives(line, partial=0):
219219
line=line[m.end():]
220220

221221
line=re.sub(r'__extension__', '', line)
222+
line=re.sub(r'__thread\b', '_Thread_local', line)
222223

223224
while (m:=COMPILER_DIRECTIVE_RE.match(line)):
224225
before, _, _, closed=m.groups()

‎Tools/c-analyzer/cpython/ignored.tsv‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ Modules/_xxinterpchannelsmodule.c - _globals -
168168

169169
Python/pyfpe.c - PyFPE_counter -
170170

171+
##-----------------------
172+
## thread-local variables
173+
174+
Python/import.c - pkgcontext -
175+
Python/pystate.c - _Py_tss_tstate -
176+
171177
##-----------------------
172178
## should be const
173179
# XXX Make them const.

0 commit comments

Comments
 (0)