-
-
Notifications
You must be signed in to change notification settings - Fork 31.2k
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-123321: Make Parser/myreadline.c locking safe in free-threaded build #123690
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,7 @@ | |
PyAPI_DATA(PyThreadState*) _PyOS_ReadlineTState; | ||
PyThreadState *_PyOS_ReadlineTState = NULL; | ||
|
||
static PyThread_type_lock _PyOS_ReadlineLock = NULL; | ||
static PyMutex _PyOS_ReadlineLock; | ||
|
||
int (*PyOS_InputHook)(void) = NULL; | ||
|
||
|
@@ -373,34 +373,22 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) | |
size_t len; | ||
|
||
PyThreadState *tstate = _PyThreadState_GET(); | ||
if (_PyOS_ReadlineTState == tstate) { | ||
if (_Py_atomic_load_ptr_relaxed(&_PyOS_ReadlineTState) == tstate) { | ||
PyErr_SetString(PyExc_RuntimeError, | ||
"can't re-enter readline"); | ||
return NULL; | ||
} | ||
|
||
|
||
// GH-123321: We need to acquire the lock before setting | ||
// _PyOS_ReadlineTState, otherwise the variable may be nullified by a | ||
// different thread. | ||
Py_BEGIN_ALLOW_THREADS | ||
PyMutex_Lock(&_PyOS_ReadlineLock); | ||
_Py_atomic_store_ptr_relaxed(&_PyOS_ReadlineTState, tstate); | ||
if (PyOS_ReadlineFunctionPointer == NULL) { | ||
PyOS_ReadlineFunctionPointer = PyOS_StdioReadline; | ||
} | ||
|
||
if (_PyOS_ReadlineLock == NULL) { | ||
_PyOS_ReadlineLock = PyThread_allocate_lock(); | ||
if (_PyOS_ReadlineLock == NULL) { | ||
PyErr_SetString(PyExc_MemoryError, "can't allocate lock"); | ||
return NULL; | ||
} | ||
} | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the GIL though? We don't release it for the regular build? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, that's right. We don't need it for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does PyMutex take the GIL if it's called outside of the GIL? Not next to the code atm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can call If the GIL is currently held AND If the GIL is not currently held, then it's not touched. |
||
|
||
// GH-123321: We need to acquire the lock before setting | ||
// _PyOS_ReadlineTState and after the release of the GIL, otherwise | ||
// the variable may be nullified by a different thread or a deadlock | ||
// may occur if the GIL is taken in any sub-function. | ||
PyThread_acquire_lock(_PyOS_ReadlineLock, 1); | ||
_PyOS_ReadlineTState = tstate; | ||
|
||
/* This is needed to handle the unlikely case that the | ||
* interpreter is in interactive mode *and* stdin/out are not | ||
* a tty. This can happen, for example if python is run like | ||
|
@@ -426,9 +414,8 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) | |
|
||
// gh-123321: Must set the variable and then release the lock before | ||
// taking the GIL. Otherwise a deadlock or segfault may occur. | ||
_PyOS_ReadlineTState = NULL; | ||
PyThread_release_lock(_PyOS_ReadlineLock); | ||
|
||
_Py_atomic_store_ptr_relaxed(&_PyOS_ReadlineTState, NULL); | ||
PyMutex_Unlock(&_PyOS_ReadlineLock); | ||
Py_END_ALLOW_THREADS | ||
|
||
if (rv == NULL) | ||
|
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 understand that this PR removes a race condition on this line by using
static PyMutex _PyOS_ReadlineLock;
which is initialized statically and so avoids the race condition.