GeezerVison is showing github.com — gh-159449: Fix a free-threaded asyncio race that corrupts the task lists by jblespiau · Pull Request #159483 · python/cpython
Conversation
jblespiau
commented
Oct 15, 2026
•
edited by bedevere-app
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
When a thread exits with pending asyncio Tasks, PyThreadState_Clear() moves them onto the shared interp->asyncio_tasks_head list, but each Task still records the _Py_ThreadId() of the thread that created it. unregister_task() skips the stop-the-world pause whenever that id matches the current thread. That also happens for a later thread that reuses the exited thread's pthread_t, and for destructors that run later in PyThreadState_Clear(). The resulting unlocked llist_remove() calls on the shared list race with each other and can leave an already-removed node (next == NULL) linked into it. asyncio.all_tasks(), which asyncio.run() calls on shutdown, then crashes with SIGSEGV in _asyncio_all_tasks().
Record the unique id (PyThreadState.id) of the creating thread state instead, and only take the lock-free path while that thread state is not being cleared.
Also drop the assertion in add_tasks_llist() that every linked Task is pending. A Task's state is set before unregister_task() unlinks it, and that removal can be waiting for the stop-the-world pause during which the list is walked. add_one_task() already filters out Tasks that are done.
3.14 fix is in #159451 that we can reopen if this one is accepted.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
…ask lists
When a thread exits with pending asyncio Tasks, PyThreadState_Clear() moves them
onto the shared interp->asyncio_tasks_head list, but each Task still records the
_Py_ThreadId() of the thread that created it. unregister_task() skips the
stop-the-world pause whenever that id matches the current thread. That also
happens for a later thread that reuses the exited thread's pthread_t, and for
destructors that run later in PyThreadState_Clear(). The resulting unlocked
llist_remove() calls on the shared list race with each other and can leave an
already-removed node (next == NULL) linked into it. asyncio.all_tasks(), which
asyncio.run() calls on shutdown, then crashes with SIGSEGV in
_asyncio_all_tasks().
Record the unique id (PyThreadState.id) of the creating thread state instead,
and only take the lock-free path while that thread state is not being cleared.
Also drop the assertion in add_tasks_llist() that every linked Task is pending.
A Task's state is set before unregister_task() unlinks it, and that removal can
be waiting for the stop-the-world pause during which the list is walked.
add_one_task() already filters out Tasks that are done.
|
All commit authors signed the Contributor License Agreement.

|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Hi,
is there anything I could do to help unblock this PR? I don't know what is the expected review time for such bug-fixes. I don't know if they are reviewed in bulk every N-weeks or if we don't hear back after a few days it's a bad sign.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ncio-task-unregister-race
|
Updated and rebased on HEAD.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
I think it's done, and I rebased on head again. Github is showing me it's "processing updates".
if this version still need changes, maybe it would be easier if a Python maintainer takes over as they are more familar with the tooling, tests, code, etc. As there is a reproducer, it should already help quite a lot.
the test passes after the CL, and fails on the assert Assertion 'task->task_state == STATE_PENDING' failed (Aborted). from head, and with this removed, it fails with Fatal Python error: Segmentation fault.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
Thanks @jblespiau for the PR, and @kumaraditya303 for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14, 3.15.
🐍🍒⛏🤖
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Sorry, @jblespiau and @kumaraditya303, I could not cleanly backport this to 3.14 due to a conflict.
Please backport manually with cherry_picker, see the devguide for more information.
cherry_picker aaaf61ca62f0924d7b5134c2d237313d8be2215c 3.14
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️
Hi! The buildbot AMD64 FreeBSD Refleaks 3.15 (tier-3) has failed when building commit 3619efa.
What do you need to do:
- Don't panic.
- Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
- Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/2056/builds/487) and take a look at the build logs.
- Check if the failure is related to this commit (3619efa) or if it is a false positive.
- 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/2056/builds/487
Failed tests:
- test.test_asyncio.test_ssl
Failed subtests:
- test_remote_shutdown_receives_trailing_data - test.test_asyncio.test_ssl.TestSSL.test_remote_shutdown_receives_trailing_data
Summary of the results of the build (if available):
==
Click to see traceback logs
Traceback (most recent call last):
File "/home/buildbot/buildarea/3.15.opsec-fbsd14.refleak/build/Lib/test/test_asyncio/test_ssl.py", line 1430, in test_remote_shutdown_receives_trailing_data
self.loop.run_until_complete(client(srv.addr))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/home/buildbot/buildarea/3.15.opsec-fbsd14.refleak/build/Lib/asyncio/base_events.py", line 730, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/home/buildbot/buildarea/3.15.opsec-fbsd14.refleak/build/Lib/test/test_asyncio/test_ssl.py", line 1414, in client
await future
File "/home/buildbot/buildarea/3.15.opsec-fbsd14.refleak/build/Lib/test/test_asyncio/test_ssl.py", line 1422, in wrapper
meth(sock)
~~~~^^^^^^
File "/home/buildbot/buildarea/3.15.opsec-fbsd14.refleak/build/Lib/test/test_asyncio/test_ssl.py", line 1368, in server
self.assertEqual(data_len, CHUNK * SIZE)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 0 != 4194304
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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.
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.
|