-
-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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
siginterrupt with flag=False is reset when signal received #52601
Comments
The effect of signal.siginterrupt(somesig, False) is reset the first time a that signal is received. This is not the documented behaviour, and I do not think this is a desireable behaviour. It renders siginterrupt effectively useless at providing the robustness against EINTR it is intended to provide. Attached is a fairly simple program to show this using SIGWINCH: run it in a resizeable terminal, and resize it twice. Notice that on the second terminal resize (i.e. the second SIGWINCH signal) the program crashes with an EINTR from the os.read. A partial workaround for the problem is to call signal.siginterrupt(somesig, False) again inside your signal handler, but it's very fragile. It depends on Python getting a chance to run the Python function registered by the signal.signal call, but this is not guaranteed. If there's frequent IO, that workaround might suffice. For the sig-test.py example attached to this bug, it doesn't (try it). The cause seems to be that signal_handler in signalmodule.c unconditionally does PyOS_setsig(sig_num, signal_handler) [except for SIGCHLD], which unconditionally invokes siginterrupt(sig, 1). A possible fix would be to add a 'int siginterrupt_flag;' to the Handlers array, and arrange for that value to be passed instead of the hard-coded 1. Another might be to not call PyOS_setsig from signal_handler at all -- I'm not sure why it is trying to reinstall itself, but perhaps there's some issue there I'm not aware of. |
Actually, siginterrupt shouldn't be used.
Because signal.signal might be implemented with sigaction() or signal() and the latter resets the default signal handler when the handler is called. This means that if your system doesn't support sigaction and and you don't reinstall it, then the handler will only get called the first time. The problem you describe can happen with both sigaction and signal : sigaction:
signal:
So the simple fix when sigaction is available is simply to not call PyOS_setsig() from signal_handler. |
Attached are two patches:
after:
$ ./python Lib/test/regrtest.py test_signal
test_signal
test test_signal failed -- Traceback (most recent call last):
File "/home/cf/python/trunk/Lib/test/test_signal.py", line 299, in test_siginterrupt_off
self.assertEquals(i, False)
AssertionError: True != False 1 test failed:
with patch and updated test:
$ ./python Lib/test/regrtest.py test_signal
test_signal
1 test OK. Of course, this also corrects the problem with sig-test.py, the terminal can be resized indefinitely. |
Your patches look good to me. (They don't fix platforms without sigaction, but as you say they probably don't have siginterrupt, and even if they do they will still have an unfixable race.) What's the next step? I can't see an button to add this issue to the "Show Needing Review" queue. |
Will the modified test fail on platforms that don't define HAVE_SIGACTION? |
Well, in theory, if the system has siginterrupt but not sigaction, it will fail. But as said, I don't think it's possible, see man siginterrupt: and the test right now has this at the beginning: So it pretty much assumes that any Unix system has siginterrupt, hence sigaction, so it should be safe. |
Are there any platforms that define HAVE_SIGINTERRUPT but that do not define HAVE_SIGACTION? If there are, then yes I expect they would fail that test. It would be a shame to delay this fix just because it doesn't fix all platforms... is it possible to mark that test as a known failure on a platform with siginterrupt but without sigaction? My initial comment outlined a change that would work for all platforms, but would be more complex. (I think the signal_noreinstall.diff change would be good to make even with the more complex approach, though.) |
FWIW, comparing the "change history" sections of <http://www.opengroup.org/onlinepubs/000095399/functions/siginterrupt.html\> and <http://www.opengroup.org/onlinepubs/000095399/functions/sigaction.html\> suggests that sigaction predates siginterrupt, but it's a wild and varied world out there. |
Well, I just think that the probability of having siginterrupt without sigaction is far less than having a Unix system without siginterrupt (which the current test_signal assumes). Or just drop the patch for the test, it honestly doesn't bother me ;-) |
Yes, my question was directed at finding out if there were any platforms on which we'd have to add an additional skip (which would mean refactoring that test into two tests). But if the buildbots are all happy after it is applied we can probably choose to not worry about it until/if we get a bug report. |
Only if they also have siginterrupt, which seems unlikely (as neologix explained). The implemented behavior on such platforms is unmodified from current trunk, while the test requires new behavior. I think it's probably worth testing this new behavior separately from the test for the existing behavior anyway, for the sake of clarity. If it turns out there's a platform with siginterrupt but not sigaction, then that'll also let the test be skipped there, which is nice (though this case seems unlikely to come up). I'm not exactly sure how we will know if it is expected to fail, though. I don't think This quirk probably also bears a mention in the siginterrupt documentation. A few other thoughts on the code nearby:
Aside from those points, the fix seems correct and good. |
I think that it's possible, on certain systems, that setting a signal handler for SIGCHLD while there is un unwaited child (zombie) process triggers a new sending of SIGCHLD. If a signal handler was set for SIGCLD, then a signal would The SIGCHLD signal on the other hand was generated when So, there might exist out there a couple systems that merrily mix SIGCLD and SIGCHLD, and re-raise a SIGCHLD when setting a new handler for it when unwaited children exist (which is the case in signal_handle, since child processes can't be waited for before the main thread gets to execute the handler it set up). |
It might be useful to have the contents of pyconfig.h exposed as a dict somehow. Maybe call it sys._pyconfig_h? A less ambitious change would be to expose just HAVE_SIGACTION as e.g. signal._have_sigaction. I agree with r.david.murray that we probably don't need to bother unless a buildbot or someone reports that this test fails. |
This patch has been reviewed by both Andrew and myself, it would be nice if someone made the time to land it. The test change is unlikely to break anything, and hey, that's what buildbots are for. |
I agree that this should be landed (for 2.6 and 2.7). I think I can do it. I made some changes to the tests, though. It would be nice for someone to look those over and make sure the change still looks good. I checked everything in to the siginterrupt-reset-issue8354 branch. You can also find the diff at http://codereview.appspot.com/1134042/show |
Should be resolved in, oh, let's see, r81007, r81011, r81016, and r81018. Thanks to everyone who helped out. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: