python / python/cpython

Data race creating Tcl interpreters concurrently via `_tkinter.create` under free-threading

Abierto
#154,923 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

extension-modules topic-tkinter type-bug
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

Bug report

Bug description:

_tkinter declares Py_MOD_GIL_NOT_USED, so under --disable-gil the GIL no longer serializes calls into it. _tkinter.create() creates a new Tcl interpreter with Tcl_CreateInterp():

https://github.com/python/cpython/blob/22a6c51c94a4fde986b8964f1d36d5ec3ac20dcc/Modules/_tkinter.c#L631-L643

reached from _tkinter_create_impl:

https://github.com/python/cpython/blob/22a6c51c94a4fde986b8964f1d36d5ec3ac20dcc/Modules/_tkinter.c#L3456-L3483

_tkinter does not serialize interpreter creation, so two threads calling _tkinter.create() run Tcl_CreateInterp() concurrently. That triggers Tcl's first-time global initialization, where Tcl_MutexLock lazily initializes a static mutex. That init is not concurrency-safe, so one thread's pthread_mutex_init (write) races with another thread's pthread_mutex_lock (atomic read) on the same Tcl global.

Reproducer:

import _tkinter
from threading import Thread

def worker():
    for _ in range(20000):
        try:
            _tkinter.create(None, '', 'Tk', False, 1, False, False, None)
        except Exception:
            pass

ts = [Thread(target=worker) for _ in range(12)]
for t in ts: t.start()
for t in ts: t.join()

TSAN Report (Tested on Linux, Tcl 8.6.14, with useTk=False):

==================
WARNING: ThreadSanitizer: data race (pid=650542)
  Atomic read of size 1 at 0x72a0000145d0 by thread T2:
    #0 pthread_mutex_lock <null> 
    #1 TclCreateExecEnv /usr/src/tcl8.6-8.6.14+dfsg-1build1/generic/tclExecute.c:936:5 
    #2 cfunction_vectorcall_FASTCALL /cpython/Objects/methodobject.c:449:24 
    #3 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 
    #4 PyObject_Vectorcall /cpython/Objects/call.c:327:12 
    #5 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:768:11 
    #6 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1906:35  

  Previous write of size 1 at 0x72a0000145d0 by thread T1 (mutexes: write M0):
    #0 pthread_mutex_init <null> 
    #1 Tcl_MutexLock /usr/src/tcl8.6-8.6.14+dfsg-1build1/unix/tclUnixThrd.c:430:6 
    #2 cfunction_vectorcall_FASTCALL /cpython/Objects/methodobject.c:449:24 
    #3 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 
    #4 PyObject_Vectorcall /cpython/Objects/call.c:327:12 
    #5 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:768:11 
    #6 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1906:35  

  Location is heap block of size 16384 at 0x72a000014000 allocated by thread T1:
    #0 malloc <null> 
    #1 GetBlocks /usr/src/tcl8.6-8.6.14+dfsg-1build1/generic/tclThreadAlloc.c:1044:17 
    #2 cfunction_vectorcall_FASTCALL /cpython/Objects/methodobject.c:449:24 
    #3 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 
    #4 PyObject_Vectorcall /cpython/Objects/call.c:327:12 
    #5 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:768:11 
    #6 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1906:35 

  Mutex M0 (0x7fffb1dff7a0) created at:
    #0 pthread_mutex_lock <null>
    #1 Tcl_MutexLock /usr/src/tcl8.6-8.6.14+dfsg-1build1/unix/tclUnixThrd.c:423:2 
    #2 _PyImport_RunModInitFunc /cpython/./Python/importdl.c:436:19 
    #3 import_run_extension /cpython/Python/import.c:2167:14
    #4 _imp_create_dynamic_impl /cpython/Python/import.c:5565:11 
    #5 _imp_create_dynamic /cpython/Python/clinic/import.c.h:489:20 
    #6 cfunction_vectorcall_FASTCALL /cpython/Objects/methodobject.c:449:24 
    #7 _PyVectorcall_Call /cpython/Objects/call.c:273:16 
    #8 _PyObject_Call /cpython/Objects/call.c:348:16
    #9 PyObject_Call /cpython/Objects/call.c:373:12
    #10 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2831:38 

SUMMARY: ThreadSanitizer: data race (/cpython/cpython-tsan/bin/python3.16t+0xfad6e)  in pthread_mutex_lock
==================

On macOS with Tcl/Tk 9.0 the same reproducer surfaces the _tkinter module-global data races (PyOS_InputHook, tcl_lock). With useTk=True it crashes inside Tk's own display initialization.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-156342

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza en Modules/_tkinter.c, en Tcl_CreateInterp y _tkinter_create_impl, y luego reproduce las llamadas concurrentes con una compilación de CPython free-threaded usando ThreadSanitizer. Compara la condición de carrera de Tcl en Linux con las condiciones de carrera de ámbito global del módulo en macOS descritas en el informe. Se considera terminado cuando la creación del intérprete ya no informa de las condiciones de carrera identificadas ni se bloquea con el reproductor indicado; gh-156342 ya está enlazado como contexto.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
c, python
Área
desktop, operating-systems
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.