diff options
-rw-r--r-- | .github/workflows/build.yml | 11 | ||||
-rw-r--r-- | .github/workflows/reusable-tsan.yml | 51 | ||||
-rw-r--r-- | Lib/test/test_concurrent_futures/util.py | 4 | ||||
-rw-r--r-- | Lib/test/test_logging.py | 4 | ||||
-rw-r--r-- | Python/thread_pthread.h | 4 |
5 files changed, 74 insertions, 0 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9c3994..cd56c7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -471,6 +471,15 @@ jobs: - name: Tests run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" + build_tsan: + name: 'Thread sanitizer' + needs: check_source + if: needs.check_source.outputs.run_tests == 'true' + uses: ./.github/workflows/reusable-tsan.yml + with: + config_hash: ${{ needs.check_source.outputs.config_hash }} + options: ./configure --config-cache --with-thread-sanitizer --with-pydebug + all-required-green: # This job does nothing and is only used for the branch protection name: All required checks pass if: always() @@ -485,6 +494,7 @@ jobs: - build_windows - test_hypothesis - build_asan + - build_tsan runs-on: ubuntu-latest @@ -513,6 +523,7 @@ jobs: build_ubuntu_ssltests, build_windows, build_asan, + build_tsan, ' || '' }} diff --git a/.github/workflows/reusable-tsan.yml b/.github/workflows/reusable-tsan.yml new file mode 100644 index 0000000..96a9c1b --- /dev/null +++ b/.github/workflows/reusable-tsan.yml @@ -0,0 +1,51 @@ +on: + workflow_call: + inputs: + config_hash: + required: true + type: string + options: + required: true + type: string + +jobs: + build_tsan_reusable: + name: 'Thread sanitizer' + runs-on: ubuntu-22.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - name: Runner image version + run: echo "IMAGE_VERSION=${ImageVersion}" >> $GITHUB_ENV + - name: Restore config.cache + uses: actions/cache@v4 + with: + path: config.cache + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }} + - name: Install Dependencies + run: | + sudo ./.github/workflows/posix-deps-apt.sh + sudo apt install -y clang + # Reduce ASLR to avoid TSAN crashing + sudo sysctl -w vm.mmap_rnd_bits=28 + - name: TSAN Option Setup + run: | + echo "TSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/Tools/tsan/supressions.txt" >> $GITHUB_ENV + echo "CC=clang" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + - name: Add ccache to PATH + run: | + echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV + - name: Configure ccache action + uses: hendrikmuhs/ccache-action@v1.2 + with: + save: ${{ github.event_name == 'push' }} + max-size: "200M" + - name: Configure CPython + run: ${{ inputs.options }} + - name: Build CPython + run: make -j4 + - name: Display build info + run: make pythoninfo + - name: Tests + run: ./python -m test --tsan -j4 diff --git a/Lib/test/test_concurrent_futures/util.py b/Lib/test/test_concurrent_futures/util.py index dc48bec..fc6030e 100644 --- a/Lib/test/test_concurrent_futures/util.py +++ b/Lib/test/test_concurrent_futures/util.py @@ -85,6 +85,8 @@ class ProcessPoolForkMixin(ExecutorMixin): self.skipTest("ProcessPoolExecutor unavailable on this system") if sys.platform == "win32": self.skipTest("require unix system") + if support.check_sanitizer(thread=True): + self.skipTest("TSAN doesn't support threads after fork") return super().get_context() @@ -111,6 +113,8 @@ class ProcessPoolForkserverMixin(ExecutorMixin): self.skipTest("ProcessPoolExecutor unavailable on this system") if sys.platform == "win32": self.skipTest("require unix system") + if support.check_sanitizer(thread=True): + self.skipTest("TSAN doesn't support threads after fork") return super().get_context() diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 463bbc5..6bff48c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -80,6 +80,9 @@ except ImportError: skip_if_asan_fork = unittest.skipIf( support.HAVE_ASAN_FORK_BUG, "libasan has a pthread_create() dead lock related to thread+fork") +skip_if_tsan_fork = unittest.skipIf( + support.check_sanitizer(thread=True), + "TSAN doesn't support threads after fork") class BaseTest(unittest.TestCase): @@ -737,6 +740,7 @@ class HandlerTest(BaseTest): @support.requires_fork() @threading_helper.requires_working_threading() @skip_if_asan_fork + @skip_if_tsan_fork def test_post_fork_child_no_deadlock(self): """Ensure child logging locks are not held; bpo-6721 & bpo-36533.""" class _OurHandler(logging.Handler): diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index f96c57d..896230d 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -91,6 +91,10 @@ #endif #endif +/* Thread sanitizer doesn't currently support sem_clockwait */ +#ifdef _Py_THREAD_SANITIZER +#undef HAVE_SEM_CLOCKWAIT +#endif /* Whether or not to use semaphores directly rather than emulating them with * mutexes and condition variables: |