Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.3k
gh-126895: fix readline module in free-threaded build#131208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
tom-pytel commentedMar 14, 2025
ping@ZeroIntensity ,@colesbury |
ZeroIntensity left a comment
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 covers free-threading, but after re-reading some of my analysis from back in November, I'm pretty sure it's the GNU readline library itself that's not thread-safe. Critical sections areper-interpreter, so this will continue to race in subinterpreters. I'd rather handle both cases in one fix.
That said, I'm not sure whether it's unsafe to callanyreadline function concurrently, or just the same function across multiple threads. I suspect its the former. In that case, let's go with a global lock around readline calls, but otherwise we can go with a localstatic PyMutex to serialize single calls.
tom-pytel commentedMar 14, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
What I meant above is that
You can call across threads and it won't crash because it is synchronized with GIL, you will just get wrong results. But if you called free-threaded it would crash, now it just behaves like not free-threaded and doesn't crash.
Could do that, but is that safe if one call dies in an unexpected way and mutex is left locked? |
ZeroIntensity commentedMar 14, 2025
|
tom-pytel commentedMar 14, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
The single lock on
I was under the impression that critical sections, as opposed to pure "Critical sections avoid deadlocks by implicitly suspending active critical sections and releasing the locks during calls toPyEval_SaveThread(). WhenPyEval_RestoreThread() is called, the most recent critical section is resumed, and its locks reacquired. This means the critical section API provides weaker guarantees than traditional locks ..." |
ZeroIntensity commentedMar 14, 2025
Critical sections are based off an object's |
tom-pytel commentedMar 14, 2025
So what's the verdict then wrt this, changes? |
ZeroIntensity commentedMar 14, 2025
Let's just confirm that we really do need a global lock here; I don't want to drastically inhibit scaling if we don't have to. |
tom-pytel commentedMar 14, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Short: Yes it needs to be global because readline is not thread-safe. But its only applied on things that actually call into readline. |
colesbury left a comment
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.
Adding critical sections seems fine to me:
- I don't think the new tests are worth it
- There are a bunch of functions that don't have critical sections: readline.write_history_file, readline.append_history_file, readline.set_history_length, ...
Uh oh!
There was an error while loading.Please reload this page.
ZeroIntensity commentedMar 15, 2025
I'm fine with critical sections too, I'm just worried about basically enabling the GIL for all usage of
I think you're misinterpreting what I mean a little. "Thread-safety" is a blanket term; I mean that we might not need locks synchronizing theentire module. For example, it makes sense to me that |
colesbury commentedMar 15, 2025
Adding a lock to the readline module is not like enabling the GIL. The GIL affects everything in the process, including things that don't touch the readline module. The readline lock only affects things that use readline. Using multiple threads to concurrently muck around with readline is not a real use case that we should optimize for. So far the only time I've seen anyone try to use readline from multiple threads at all is when fuzzing CPython.
In general, we should prefer the simplest approach to thread safety that is efficient. In this case, that means a single lock for the entire readline module. |
tom-pytel commentedMar 15, 2025
One potential issue. |
colesbury commentedMar 15, 2025
|
bedevere-bot commentedMar 17, 2025
🤖 New build scheduled with the buildbot fleet by@colesbury for commit0dca261 🤖 Results will be shown at: https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F131208%2Fmerge If you want to schedule another build, you need to add the🔨 test-with-refleak-buildbots label again. |
863d54c intopython:mainUh oh!
There was an error while loading.Please reload this page.
colesbury commentedMar 17, 2025
Thanks Tom! I'm not going to backport this to 3.13. I think it's unlikely that anyone is going to try to use the readline module from multiple threads concurrently outside of fuzzing. |
…31208)The underlying readline library is not thread-safe so this adds `@critical_section` to functions that use it.
…31208)The underlying readline library is not thread-safe so this adds `@critical_section` to functions that use it.
Uh oh!
There was an error while loading.Please reload this page.
This is nothing more than adding
@critical_sectionto functions which use the readline library. It is not thread-safe (because readline is not thread-safe), but it doesn't crash like before.The test
test_free_threading_doctest_difflibis probably overkill, but included at first to show that it works. Can comment out or remove as desired.Also, without
test_free_threading_doctest_difflib(that uses stuff which is not free-thread safe),test_readlinefails without crashing with--parallel-threads, where previously it crashed. Also without it doesn't complain when tested with TSAN.Subinterpreters are not an issue because
readlinejust doesn't load in them:module readline does not support loading in subinterpretersreadline.set_completer_delimsin threads in a free-threaded build #126895