GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-124878: Fix race conditions during interpreter finalization by colesbury · Pull Request #130649 · python/cpython

Conversation

@colesbury

@colesbury colesbury commented Feb 27, 2025 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

The PyThreadState field gains a reference count field to avoid issues with PyThreadState being a dangling pointer to freed memory. The refcount starts with a value of two: one reference is owned by the interpreter's linked list of thread states and one reference is owned by the OS thread. The reference count is decremented when the thread state is removed from the interpreter's linked list and before the OS thread calls PyThread_hang_thread(). The thread that decrements it to zero frees the PyThreadState memory.

The holds_gil field is moved out of the _status bit field, to avoid a data race where on thread calls PyThreadState_Clear(), modifying the _status bit field while the OS thread reads holds_gil when attempting to acquire the GIL.

The PyThreadState.state field now has _Py_THREAD_SHUTTING_DOWN as a possible value. This corresponds to the _PyThreadState_MustExit() check. This avoids race conditions in the free threading build when checking _PyThreadState_MustExit().

  • Issue: race condition in threading when interpreter finalized while daemon thread runs (thread sanitizer identified) #124878

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

lagergren-oai reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@colesbury
pythongh-124878: Fix race conditions during interpreter finalization
f9e5173
The PyThreadState field gains a reference count field to avoid
issues with PyThreadState being a dangling pointer to freed memory.
The refcount starts with a value of two: one reference is owned by the
interpreter's linked list of thread states and one reference is owned by
the OS thread. The reference count is decremented when the thread state
is removed from the interpreter's linked list and before the OS thread
calls `PyThread_hang_thread()`. The thread that decrements it to zero
frees the `PyThreadState` memory.

The `holds_gil` field is moved out of the `_status` bit field, to avoid
a data race where on thread calls `PyThreadState_Clear()`, modifying the
`_status` bit field while the OS thread reads `holds_gil` when
attempting to acquire the GIL.

The `PyThreadState.state` field now has `_Py_THREAD_SHUTTING_DOWN` as a
possible value. This corresponds to the `_PyThreadState_MustExit()`
check. This avoids race conditions in the free threading build when
checking `_PyThreadState_MustExit()`.
@bedevere-app bedevere-app Bot mentioned this pull request Feb 27, 2025
race condition in threading when interpreter finalized while daemon thread runs (thread sanitizer identified) #124878
Closed
@colesbury
colesbury marked this pull request as ready for review February 27, 2025 19:36
@colesbury
colesbury requested a review from ericsnowcurrently as a code owner February 27, 2025 19:36
@bedevere-app bedevere-app Bot added the awaiting core review label Feb 27, 2025
@colesbury
colesbury requested review from gpshead and mpage February 27, 2025 19:36
vstinner
vstinner approved these changes Feb 28, 2025
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Avoiding dangling pointers close a whole category of bugs. Using a reference count is a nice solution for that. I also like the fact that Py_Finalize() now sets threads state to "shutting down": it's more explicit like that.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting core review labels Feb 28, 2025
@vstinner

vstinner commented Feb 28, 2025

Copy link
Copy Markdown
Member

By the way, this change may also fix the old crash #110052 (test_4_daemon_threads).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@colesbury
Merge branch 'main' into pythongh-124878-tstate-refcount
aab946e
@colesbury

colesbury commented Feb 28, 2025

Copy link
Copy Markdown
Contributor Author

Yeah, I think it should fix test_4_daemon_threads as well. I'm using the repro from that issue:

./python -m test test_threading -m test_4_daemon_threads -j50 -F --fail-env-changed

And also a small patch to ceval_gil.c to make the crash more likely to occur on my machine.

On main (with the patch to ceval_gil.c), I see a crash pretty quickly after ~100 iterations on both the default and free threaded builds.

With this PR, I haven't seen any failures in 25,000 iterations (~10 minutes).

Patch to ceval_gil.c
diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c
index 2c1cc17b2ff..e14f1a8afa2 100644
--- a/Python/ceval_gil.c
+++ b/Python/ceval_gil.c
@@ -306,6 +306,8 @@ take_gil(PyThreadState *tstate)
         _PyThreadState_HangThread(tstate);
     }

+    usleep(10);
+
     assert(_PyThreadState_CheckConsistency(tstate));
     PyInterpreterState *interp = tstate->interp;
     struct _gil_runtime_state *gil = interp->ceval.gil;
vstinner and gpshead reacted with hooray emoji
All reactions
  • 🎉 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@colesbury
colesbury merged commit 052cb71 into python:main Mar 6, 2025
@bedevere-app bedevere-app Bot removed the awaiting merge label Mar 6, 2025
@colesbury
colesbury deleted the gh-124878-tstate-refcount branch March 6, 2025 15:38
@bedevere-bot

bedevere-bot commented Mar 6, 2025

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot s390x RHEL8 3.x (tier-3) has failed when building commit 052cb71.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/509/builds/8563) and take a look at the build logs.
  4. Check if the failure is related to this commit (052cb71) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/509/builds/8563

Failed tests:

  • test.test_multiprocessing_spawn.test_manager

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 266, in serve_client
    raise ke
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 260, in serve_client
    obj, exposed, gettypeid = id_to_obj[ident]
                              ~~~~~~~~~^^^^^^^
KeyError: '3ff9859f260'
---------------------------------------------------------------------------
Timeout (0:20:00)!
Thread 0x000003ffb74f7270 (most recent call first):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/popen_fork.py", line 28 in poll
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/popen_fork.py", line 44 in wait
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 149 in join
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 623 in _callCleanup
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 697 in doCleanups
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 664 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 716 in __call__
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 122 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 84 in __call__
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 122 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 84 in __call__
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/runner.py", line 259 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 84 in _run_suite
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 42 in run_unittest
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 162 in test_func
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 118 in regrtest_runner
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 165 in _load_run_test
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 210 in _runtest_env_changed_exc
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 319 in _runtest
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 348 in run_single_test
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/worker.py", line 92 in worker_process
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/worker.py", line 127 in main
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/worker.py", line 131 in <module>
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/runpy.py", line 88 in _run_code
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/runpy.py", line 198 in _run_module_as_main


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 266, in serve_client
    raise ke
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 260, in serve_client
    obj, exposed, gettypeid = id_to_obj[ident]
                              ~~~~~~~~~^^^^^^^
KeyError: '3ff985fb340'
---------------------------------------------------------------------------
k


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 313, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/_test_multiprocessing.py", line 1625, in f
    woken.release()
    ~~~~~~~~~~~~~^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 1067, in release
    return self._callmethod('release')
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 848, in _callmethod
    raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError: 
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 264, in serve_client
    self.id_to_local_proxy_obj[ident]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
KeyError: '3ff89835aa0'


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 266, in serve_client
    raise ke
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 260, in serve_client
    obj, exposed, gettypeid = id_to_obj[ident]
                              ~~~~~~~~~^^^^^^^
KeyError: '3ff89835aa0'
---------------------------------------------------------------------------
Timeout (0:20:00)!
Thread 0x000003ff9f377270 (most recent call first):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/popen_fork.py", line 28 in poll
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/popen_fork.py", line 44 in wait
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 149 in join
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 623 in _callCleanup
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 697 in doCleanups
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 664 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/case.py", line 716 in __call__
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 122 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 84 in __call__
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 122 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/suite.py", line 84 in __call__
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/unittest/runner.py", line 259 in run
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 84 in _run_suite
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 42 in run_unittest
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 162 in test_func
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 118 in regrtest_runner
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 165 in _load_run_test
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 210 in _runtest_env_changed_exc
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 319 in _runtest
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/single.py", line 348 in run_single_test
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/worker.py", line 92 in worker_process
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/worker.py", line 127 in main
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/libregrtest/worker.py", line 131 in <module>
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/runpy.py", line 88 in _run_code
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/runpy.py", line 198 in _run_module_as_main


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 313, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/_test_multiprocessing.py", line 1625, in f
    woken.release()
    ~~~~~~~~~~~~~^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 1067, in release
    return self._callmethod('release')
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 848, in _callmethod
    raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError: 
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 264, in serve_client
    self.id_to_local_proxy_obj[ident]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
KeyError: '3ff985fb340'


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 313, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/test/_test_multiprocessing.py", line 1625, in f
    woken.release()
    ~~~~~~~~~~~~~^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 1067, in release
    return self._callmethod('release')
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 848, in _callmethod
    raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError: 
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-rhel8-s390x/build/Lib/multiprocessing/managers.py", line 264, in serve_client
    self.id_to_local_proxy_obj[ident]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
KeyError: '3ff9859f260'
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner mentioned this pull request Mar 13, 2025
test_threading.test_4_daemon_threads() crash randomly #110052
Closed
@kumaraditya303 kumaraditya303 mentioned this pull request Apr 19, 2025
PyThreadState_Clear, tstate_activate/_PyThreadState_Attach and drop_gil race race on TSAN #121751
Closed
@kumaraditya303 kumaraditya303 mentioned this pull request Jun 21, 2025
TSan: data race with _PyThreadState_Attach and _PyThreadState_Detach #116910
Closed
@kumaraditya303 kumaraditya303 mentioned this pull request Oct 15, 2025
TSAN failure with free-threaded build, test_daemon_threads_fatal_error #140138
Closed
This was referenced Aug 13, 2026
[sanitizer] Stress workloads / fq http_api ThreadSanitizer / data race in free at Python interpreter shutdown (release-tsan) ydb-platform/ydb#49949
Closed
[sanitizer] Stress workloads / fq http_api ThreadSanitizer / data race in free at Python interpreter shutdown (release-tsan) ydb-platform/ydb#50414
Closed
encukou
encukou reviewed Sep 9, 2026
View reviewed changes
Comment thread Python/ceval_gil.c
Comment on lines 403 to 404
/* tstate could be a dangling pointer, so don't pass it to
drop_gil(). */

@encukou encukou Sep 9, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is now out of date, right?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@encukou encukou encukou left review comments
@vstinner vstinner vstinner approved these changes
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently is a code owner
@gpshead gpshead Awaiting requested review from gpshead
@mpage mpage Awaiting requested review from mpage

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@colesbury @vstinner @bedevere-bot @encukou
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.