-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
gh-123961: make curses
global flags free-threaded friendly
#125000
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-123961: make curses
global flags free-threaded friendly
#125000
Conversation
ee22931
to
5a119c7
Compare
…-threading-friendly-123961
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.
I couldn't find anything in here to complain about 😉, LGTM!
If you cannot have two instances of the |
From what I understand, it'd be possible to access the curses extension state from different threads (not necessarily processes). Or am I wrong? Ok I know why I didn't get the error in the terminal; it's because the module is still single-phase for now. Once it's multi-phase, I'll also get an error if I want to load it more than once per process (namely, I won't need to use the |
I suggest to first make the _curses extension use the multiphase init API, and then look on how to make it safe to be used on a Free Threaded build. |
Hmm, multiphase init shouldn't affect this. IIUC, module states need to be locked for free-threaded extensions. |
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.
I'm not familiar with the curses module, but I'm a bit skeptical of this approach. Is curses thread safe?
static int | ||
cursesmodule_exec(PyObject *module) | ||
{ | ||
if (_Py_atomic_load_int(&curses_module_loaded)) { |
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.
The atomics here don't really do anything useful because you are not atomically checking and setting the variable together. Either use an atomic compare-exchange or don't use atomics at all.
Within an interpreter, the import lock will prevent multiple concurrent executions of cursesmodule_exec
, but I'm not sure about what happens with multiple interpreters.
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.
FWIW, curses doesn't support subinterpreters, as indicated by the m_size
being -1
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.
Within an interpreter, the import lock will prevent multiple concurrent executions of cursesmodule_exec
Oh ok. Thanks for the tip. I wasn't really sure on this one. I'll revert this change later
AFAIK, no (at least I'm clearly not an expert in free-threading nor in concurrency in general, so I really appreciate your help! |
I think it's worth figuring out what sort of locking and thread-safety strategy we should use for |
I think I'll close this PR for now. We'll likely need to revisit this question in a separate issue and PRs. |
We allow global variables indicating whether the underlying curses functions have been called to be safely used in free-threaded builds.
In addition, since
curses
manages the screen, only onecurses
module can be loaded per process (i.e the exec() function is executed only once and anImportError
is raised if the user attempts to load multiplecurses
modules within the same process).This can be tested via:
./python -c "import sys; import subprocess; subprocess.run(['./Programs/_testembed', 'test_repeated_init_exec', 'import curses'])"
_cursesmodule.c
to fix reference leaks #123961