From a81eeec812cae2dbb552b8b1db072a31ed01a596 Mon Sep 17 00:00:00 2001 From: jg Date: Mon, 2 Nov 2026 08:50:36 +0000 Subject: [PATCH 1/3] Docs: add a free-threading section on testing with ThreadSanitizer There was no documentation on using ThreadSanitizer with the free-threaded build beyond the configure option. Describe how to build, how extensions are instrumented, how to read a report, and how to run CPython's TSan tests, and note that TSan does not detect logic races in Python code. --- Doc/howto/free-threading-extensions.rst | 69 +++++++++++++++++++++++++ Doc/using/configure.rst | 3 +- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index ad0578df0a2702..ac1d58b6080249 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -411,6 +411,75 @@ field (or another synchronization primitive) to your object struct. an additional one. +.. _freethreading-tsan: + +Testing with ThreadSanitizer +============================ + +Data races in C code are easy to introduce and hard to reproduce. +`ThreadSanitizer `_ (TSan) is +a data race detector supported by Clang and GCC that reports conflicting +accesses to the same memory location from different threads that are not +ordered by a lock or an atomic operation. It is a good way to find missing +locks, critical sections or atomic operations in an extension. + +.. note:: + + TSan reports data races in C code, both in CPython and in extensions. It + does not report races in the logic of Python code, such as two threads + running ``counter = counter + 1`` on a shared object. Each of those + operations is synchronized internally, so the program is free of data races + even though it loses updates. + +To use it, build a free-threaded interpreter with TSan enabled:: + + ./configure --disable-gil --with-thread-sanitizer --with-pydebug + make + +``--with-pydebug`` is optional, but enables assertions that catch some +additional errors. See :option:`--with-thread-sanitizer`. TSan can also be +used with a build that has the GIL enabled, but the GIL orders most +accesses, so a free-threaded build is much more informative. + +The build records ``-fsanitize=thread`` in its compiler and linker flags +(see :func:`sysconfig.get_config_var`), so extensions built with that +interpreter by build tools that use these flags, such as ``setuptools``, are +instrumented automatically. If you build an extension by hand, pass +``-fsanitize=thread`` when compiling and linking. Code that is not +instrumented, such as third-party libraries built without TSan, is not +checked. + +Then run your tests with the TSan interpreter, using several threads that +exercise the same objects. When a race is found, TSan prints the conflicting +accesses with stack traces and a summary line. For example, for an extension +function that increments a global variable without synchronization:: + + WARNING: ThreadSanitizer: data race (pid=36446) + Write of size 8 at 0x00010f1f8108 by thread T2: + #0 bump racy.c:8 (racy.cpython-316td-darwin.so:arm64+0x7a8) + ... + Previous write of size 8 at 0x00010f1f8108 by thread T1: + #0 bump racy.c:8 (racy.cpython-316td-darwin.so:arm64+0x7a8) + ... + Location is global 'counter' at 0x00010f1f8108 (racy.cpython-316td-darwin.so+0x8108) + + SUMMARY: ThreadSanitizer: data race racy.c:8 in bump + +The TSan runtime can be configured with the ``TSAN_OPTIONS`` environment +variable, for example ``TSAN_OPTIONS="suppressions=path/to/suppressions.txt"`` +to ignore known issues. The CPython source tree has a suppressions file for +the free-threaded build in :source:`Tools/tsan/suppressions_free_threading.txt`. +On Linux, TSan may fail to start on kernels with high address space layout +randomization; if that happens, lowering it with +``sudo sysctl -w vm.mmap_rnd_bits=28`` is a common fix. + +CPython's own test suite can be run under TSan with +``./python -m test --tsan``, which runs a subset of tests that use threads. +Tests that do not use threads themselves can be run in several threads at once +with ``./python -m test --tsan-parallel --parallel-threads=N``. This is +useful if you want to check a standard library or CPython change that touches +code shared between threads. + Building Extensions for the Free-Threaded Build =============================================== diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 33d3a25df6de49..1356b1f55ce8ac 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -1084,7 +1084,8 @@ Debug options .. option:: --with-thread-sanitizer Enable ThreadSanitizer data race detector, ``tsan`` - (default is no). + (default is no). It is most useful together with :option:`--disable-gil`. + See :ref:`freethreading-tsan`. .. versionadded:: 3.13 From 6cdf257f325f3b4f28176e92803eb8df0bd41f08 Mon Sep 17 00:00:00 2001 From: jg Date: Mon, 2 Nov 2026 10:22:36 +0000 Subject: [PATCH 2/3] Docs: fix highlighting of the ThreadSanitizer examples The HOWTO sets the default highlight language to C, so the TSan report was lexed as C and raised a highlighting warning. Mark it, and the shell commands, with explicit languages. --- Doc/howto/free-threading-extensions.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index ac1d58b6080249..1a68b9205a1067 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -431,7 +431,9 @@ locks, critical sections or atomic operations in an extension. operations is synchronized internally, so the program is free of data races even though it loses updates. -To use it, build a free-threaded interpreter with TSan enabled:: +To use it, build a free-threaded interpreter with TSan enabled: + +.. code-block:: shell ./configure --disable-gil --with-thread-sanitizer --with-pydebug make @@ -452,7 +454,9 @@ checked. Then run your tests with the TSan interpreter, using several threads that exercise the same objects. When a race is found, TSan prints the conflicting accesses with stack traces and a summary line. For example, for an extension -function that increments a global variable without synchronization:: +function that increments a global variable without synchronization: + +.. code-block:: none WARNING: ThreadSanitizer: data race (pid=36446) Write of size 8 at 0x00010f1f8108 by thread T2: From 9ea1e7d40fb892ecb3794ffca5635082de125cec Mon Sep 17 00:00:00 2001 From: jg Date: Mon, 2 Nov 2026 18:48:17 +0000 Subject: [PATCH 3/3] Docs: drop the configure.rst cross-reference to the TSan section --- Doc/using/configure.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 1356b1f55ce8ac..33d3a25df6de49 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -1084,8 +1084,7 @@ Debug options .. option:: --with-thread-sanitizer Enable ThreadSanitizer data race detector, ``tsan`` - (default is no). It is most useful together with :option:`--disable-gil`. - See :ref:`freethreading-tsan`. + (default is no). .. versionadded:: 3.13