Skip to content

ThreadCache workers leaking spawner's Context on py314+ free-threaded builds#3473

Merged
A5rocks merged 19 commits into
python-trio:mainfrom
EmmanuelNiyonshuti:free-threaded-context-leak-threadcache
Jul 16, 2026
Merged

ThreadCache workers leaking spawner's Context on py314+ free-threaded builds#3473
A5rocks merged 19 commits into
python-trio:mainfrom
EmmanuelNiyonshuti:free-threaded-context-leak-threadcache

Conversation

@EmmanuelNiyonshuti

Copy link
Copy Markdown
Contributor

fixes #3472

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00000%. Comparing base (976c11b) to head (eb8320f).

Additional details and impacted files
@@               Coverage Diff               @@
##                 main        #3473   +/-   ##
===============================================
  Coverage   100.00000%   100.00000%           
===============================================
  Files             128          128           
  Lines           19423        19439   +16     
  Branches         1317         1318    +1     
===============================================
+ Hits            19423        19439   +16     
Files with missing lines Coverage Δ
src/trio/_core/_thread_cache.py 100.00000% <100.00000%> (ø)
src/trio/_tests/test_threads.py 100.00000% <100.00000%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@A5rocks A5rocks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Makes sense to me. Could you add a test? I guess just something to make sure that threads don't inherit context would work well enough.

Comment thread src/trio/_core/_thread_cache.py Outdated
Comment thread newsfragments/3472.bugfix.rst Outdated

@A5rocks A5rocks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test could be simplified I think, but looks good to me.

"https://github.com/pypy/pypy/issues/5075)"
),
)
async def test_worker_thread_context_not_leaked() -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IMO you should just check whether context gets carried over, no need for GC or whatever. Technically there could be a bug where we clear it in the frame before handing it to the user but store it elsewhere -- and you could test for that using weakref (to ensure it works on PyPy) -- but IMO not necessary.

@EmmanuelNiyonshuti EmmanuelNiyonshuti Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we could use weakref too. I leaned more towards how anyio did it.

@EmmanuelNiyonshuti

Copy link
Copy Markdown
Contributor Author

Forgive these so many commits. Not sure if you squash commits when merging, but feel free to tidy them up if needed.

@CoolCat467 CoolCat467 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good as far as I can tell, though I agree test could probably be simplified.

@EmmanuelNiyonshuti

Copy link
Copy Markdown
Contributor Author

@A5rocks what do you think about this approach? used weakref and just asserted that the object is dead.

@EmmanuelNiyonshuti

EmmanuelNiyonshuti commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

PyPy failures are now irrelevant because we don't need gc.get_referrers anymore!

@EmmanuelNiyonshuti

Copy link
Copy Markdown
Contributor Author

By the way, this was found while exercising anyio's test suite on the Python 3.14t free-threaded build. anyio has a regression test that checks for leaked references from its worker-thread pool, and running it against trio's backend surfaced this.

@A5rocks

A5rocks commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Thanks for this!

@A5rocks
A5rocks merged commit 83b96a3 into python-trio:main Jul 16, 2026
44 checks passed
@trio-bot

trio-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

Hey @EmmanuelNiyonshuti, it looks like that was the first time we merged one of your PRs! Thanks so much! 🎉 🎂

If you want to keep contributing, we'd love to have you. So, I just sent you an invitation to join the python-trio organization on Github! If you accept, then here's what will happen:

  • Github will automatically subscribe you to notifications on all our repositories. (But you can unsubscribe again if you don't want the spam.)

  • You'll be able to help us manage issues (add labels, close them, etc.)

  • You'll be able to review and merge other people's pull requests

  • You'll get a [member] badge next to your name when participating in the Trio repos, and you'll have the option of adding your name to our member's page and putting our icon on your Github profile (details)

If you want to read more, here's the relevant section in our contributing guide.

Alternatively, you're free to decline or ignore the invitation. You'll still be able to contribute as much or as little as you like, and I won't hassle you about joining again. But if you ever change your mind, just let us know and we'll send another invitation. We'd love to have you, but more importantly we want you to do whatever's best for you.

If you have any questions, well... I am just a humble Python script, so I probably can't help. But please do post a comment here, or in our chat, or on our forum, whatever's easiest, and someone will help you out!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ThreadCache workers leak the spawning thread's contextvars.Context on free-threaded builds

3 participants