-
- Notifications
You must be signed in to change notification settings - Fork 33.4k
bpo-30050: Allow disabling full buffer warnings in signal.set_wakeup_fd #4792
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
Conversation
No semantic changes.
This variable is already set to this value directly above. No semantic change.
This commit has no behavioral changes. When a send() on Windows failed in signal handler context, we used to do a lot of hoop-jumping to pass both errno and the GetLastError() back out so they could be reported. But... send() on Windows does not set errno: "[If an error occurs], a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError." -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms740149(v=vs.85).aspx This allows the code to be substantially simplified. Also, there is no concept of EINTR on Windows, and even if there was then it wouldn't be reported through errno, so there's no need to loop checking for errno == EINTR.
| This PR will be easiest to review one commit at a time: each commit is self-contained, and the first few are brush-clearing refactorings. (I think there's potential to simplify |
vstinner 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.
I like the overall change, but I have a few comments.
Would you mind to document the new optional parameter in the signal section of Doc/whatsnew/3.7.rst please?
Modules/signalmodule.c Outdated
| int send_errno; | ||
| int send_win_error; | ||
| } wakeup = {INVALID_FD, 0, 0}; | ||
| } wakeup = {INVALID_FD, 1, 0}; |
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 suggest to use C99 initializers:
wakeup = {.fd = INVALID_FD, .warn_on_full_buffer = 1, use_end=0};
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.
Done.
Modules/signalmodule.c Outdated
| static volatile struct { | ||
| sig_atomic_t fd; | ||
| int warn_on_full_buffer; | ||
| } wakeup = {INVALID_FD, 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.
wakeup = {.fd = INVALID_FD, .warn_on_full_buffer = 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.
Done.
Doc/library/signal.rst Outdated
| the byte values give you the signal numbers. This is simple, but it | ||
| can run into a problem: generally the fd will have a limited amount | ||
| of buffer space, and if too many signals arrive too quickly, the | ||
| buffer may become full, and some signals may be lost. If you use |
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 propose to add something like "if many signals are received quickly, faster that what the application can read".
I never saw this warning, there is need to scare users ;-)
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.
Tweaked the text to emphasize that this is a rare condition.
Modules/signalmodule.c Outdated
| Py_AddPendingCall(report_wakeup_write_error, | ||
| (void *)(intptr_t)errno); | ||
| if (wakeup.warn_on_full_buffer || | ||
| (errno != EWOULDBLOCK && errno != EAGAIN)) { |
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.
PEP 7: for multiline condition, please put { on a new line.
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.
Done.
| signum = signal.SIGINT | ||
| def handler(signum, frame): | ||
| pass |
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.
Please add a comment explaining that the signal handler is called but don't read pending signals from the socket.
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.
Done.
Lib/test/test_signal.py Outdated
| # Fill the send buffer | ||
| try: | ||
| while True: | ||
| write.send(b"x" * 1024) |
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.
What is send(1024 bytes) fails with BlockingIOError, but send(1 bytes) works?
I suggest to loop on write.send(b"x") instead.
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 don't think this can happen in any real system (you'd get a partial send instead), but I checked and sending 1 byte at a time is only ~2x slower, so why not. Done.
| assert_python_ok('-c', code) | ||
| | ||
| @unittest.skipIf(_testcapi is None, 'need _testcapi') | ||
| def test_warn_on_full_buffer(self): |
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.
Please try to split this test into 3 tests, and handle stderr in the parent process. Running 3 tests in the same process may have side effects between tests.
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'd rather not? Part of what I want to test is that there are no side-effects between tests :-). (As there could be e.g. if warn_on_full_buffer values leaked between different calls to set_wakeup_fd.) And the handling of stderr within the child process is copied straight from the other wakeup fd warning tests in the same file.
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.
Ok.
| { | ||
| struct _Py_stat_struct status; | ||
| static char *kwlist[] = { | ||
| "", "warn_on_full_buffer", 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.
Please write an unit test to make sure that it's not possible to pass the signal number as a keyword argument.
Dummy test like: with self.assertRaises(TypeError): signal.set_wakeup_fd(signum=signal.SIGINT)
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.
Done.
| @pitrou, @serhiy-storchaka: I would feel more confortable if this change would be double checked, since signals are complex and is a critical component. IHMO it's still ok to add the new parameter in Python 3.7. |
vstinner 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.
LGTM, just a few remaining nitpicking.
But as I wrote, I would prefer to get review of at least a second core developer.
Doc/library/signal.rst Outdated
| On Windows, the function now also supports socket handles. | ||
| | ||
| .. versionchanged:: 3.7 | ||
| Added ``warn_on_full_buffer``. |
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.
Added warn_on_full_buffer parameter.
| | ||
| def test_invalid_call(self): | ||
| with self.assertRaises(TypeError): | ||
| signal.set_wakeup_fd(signum=signal.SIGINT) |
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 suggest a short comment: "# first parameter is positional-only"
| signal.set_wakeup_fd(signum=signal.SIGINT) | ||
| | ||
| with self.assertRaises(TypeError): | ||
| signal.set_wakeup_fd(signal.SIGINT, False) |
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 suggest a short comment: "# warn_on_full_buffer is a keyword-only parameter"
| assert_python_ok('-c', code) | ||
| | ||
| @unittest.skipIf(_testcapi is None, 'need _testcapi') | ||
| def test_warn_on_full_buffer(self): |
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.
Ok.
| Ah, there are some issues on Windows. AppVeyor failed to compile: |
| Huh, weird that appveyor didn't highlight that the first time! Anyway, fixed the appeyor build (I hope) and addressed all comments. |
AppVeyor didn't run the first time. There is an heuristic to not run AppVeyor on changes which are only documentation or specific to Linux. The heuristic isn't perfect :-) |
vstinner 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.
LGTM.
Previously, if the wakeup fd's buffer overflowed, Python would
unconditionally print a warning on stderr. But for many of
set_wakeup_fd's users (e.g. Twisted, Tornado, and Trio), this is
incorrect: the way they use set_wakeup_fd, a full buffer is a totally
normal and unexceptional condition. On the other hand, for asyncio and
curio, a full buffer causes signals to be lost, so they really want to
issue a warning (at the least!).
This change lets the caller of set_wakeup_fd choose whether they would
like this warning to be printed or not.
https://bugs.python.org/issue30050