-
-
Notifications
You must be signed in to change notification settings - Fork 31.7k
bpo-46210: Fix deadlock in print. #30310
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
base: main
Are you sure you want to change the base?
Changes from all commits
dc5d787
46d4ce6
3409181
19f19b7
f5334b4
3858006
fb0bd16
8c8d9c9
a14bf74
be40192
bf9b398
5681a02
2054bfa
a8e4185
56a405f
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 |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Add :c:func:`_at_fork_reinit` handler to ``buffer->lock`` of stdout and | ||
stderr, to fix a deadlock in print. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2895,6 +2895,53 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) | |
return _PyStatus_ERR("can't initialize sys module"); | ||
} | ||
|
||
#ifdef HAVE_FORK | ||
extern int _PyIO_buffered_at_fork_reinit(PyObject *); | ||
|
||
static int | ||
stream_at_fork_reinit(PyObject *stream) | ||
{ | ||
if (stream == NULL || Py_IsNone(stream)) { | ||
return 0; | ||
} | ||
|
||
/* The buffer attribute is not part of the TextIOBase API | ||
* and may not exist in some implementations. If not present, | ||
* we have no locks to reinitialize. */ | ||
PyObject *buffer = PyObject_GetAttr(stream, &_Py_ID(buffer)); | ||
if (buffer == NULL) { | ||
PyErr_Clear(); | ||
return 0; | ||
} | ||
|
||
/* Reinitialize buffer->lock */ | ||
int ret = _PyIO_buffered_at_fork_reinit(buffer); | ||
Py_DECREF(buffer); | ||
return ret; | ||
} | ||
|
||
/* This function is called from PyOS_AfterFork_Child() to ensure that newly | ||
created child processes do not share locks with the parent. */ | ||
PyStatus | ||
_PySys_ReInitStdio(void) | ||
{ | ||
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. sys.stdin has also a buffer which has a lock which must be reinitialized at fork. 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. Cool, now |
||
PyThreadState *tstate = _PyThreadState_GET(); | ||
|
||
PyObject *sys_stdin = _PySys_GetAttr(tstate, &_Py_ID(stdin)); | ||
PyObject *sys_stdout = _PySys_GetAttr(tstate, &_Py_ID(stdout)); | ||
PyObject *sys_stderr = _PySys_GetAttr(tstate, &_Py_ID(stderr)); | ||
|
||
int reinit_stdin = stream_at_fork_reinit(sys_stdin); | ||
int reinit_stdout = stream_at_fork_reinit(sys_stdout); | ||
int reinit_stderr = stream_at_fork_reinit(sys_stderr); | ||
|
||
if (reinit_stdin < 0 || reinit_stdout < 0 || reinit_stderr < 0) { | ||
return _PyStatus_ERR("Failed to reinitialize standard streams"); | ||
} | ||
return _PyStatus_OK(); | ||
} | ||
#endif | ||
|
||
static int | ||
sys_add_xoption(PyObject *opts, const wchar_t *s) | ||
{ | ||
|
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 dislike such function test, it's fragile and may break tomorrow. I would prefer to remove it.
If you want to keep it, you should:
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.
Can you clarify why you think it's fragile? It seems useful as a regression test. I do agree that it would be more stable in a separate script.
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 is the only part I didn't change yet, b/c I wanted to check with you guys on the details.
@vstinner All the
at_fork_reinit
handlers in the codebase have your name on them, so I'll trust your judgment here. (Though I'm also curious for the same reasons @JelleZijlstra mentioned).On the topic of the current test, you said "If you want to keep it, you should..."
What's the "else" condition for that sentence? I would prefer to do whatever you think makes the most sense. I don't have any strong opinions here, aside from a desire to have that one line in there that reinitializes the lock.
Test-wise, would you prefer (1) just moving the current test to a separate script? (2) removing the current test entirely? (3) a different test with certain properties that this one doesn't have?
I'm equally happy to do any of the above. :)