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
Ctrl-C locks up the interpreter #54687
Comments
The following program is misbehaving with python3.2 import signal, time
def sighandler( arg1, arg2): print("got sigint"); assert 0
signal.signal( signal.SIGINT, sighandler)
for i in range(1000000):
print(i) I'd expect Ctrl-C to terminate the program with AssertionError and that's indeed what happens under python2.7. But with python3.2a, I get "Assertion Error" 1 out ~10 times. The other 9 times, the program locks up (goes to sleep? ps shows process status as "S"). After the program locks up, it does not respond to subsequent "Ctrl-C" presses. This is on 64-bit Ubuntu 8.04. |
I reproduce the problem, see the call stack below. In Modules/_io/buffereredio.c, the two callers to PyErr_CheckSignals() should ensure that 'self' is in a stable state, and release the lock while they run PyErr_CheckSignals(). #0 sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:85 |
Wow. The lock is precisely there so that the buffered object doesn't have to be MT-safe or reentrant. It doesn't seem reasonable to attempt to restore the file to a "stable" state in the middle of an inner routine. Also, the outer TextIOWrapper (we're talking about sys.stdout here) is not designed to MT-safe at all and is probably in an inconsistent state itself. I would rather detect that the lock is already taken by the current thread and raise a RuntimeError. I don't think it's a good idea to do buffered I/O in a signal handler. Unbuffered I/O probably works. (in a more sophisticated version, we could store pending writes so that they get committed at the end of the currently executing write) |
Here is a patch raising RuntimeError on reentrant calls to a buffered object. I haven't touched _pyio; I wonder how to do it without making it even slower. |
Would avoiding PyErr_CheckSignals() while the file object is in inconsistent state be a reasonable alternative? I am guessing that it's not that uncommon for a signal handler to need IO (e.g to log a signal). If making IO safer is not an option, then I think, this limitation needs to be documented (especially, given that this seems to be a behavior change from Python 2.x). |
No, because we'd like IO operations to be interruptible by the user
In C, it is recommended that signal handlers be minimal. In Python, That said, there's no problem doing IO as long as you're not doing
Perhaps the IO documentation needs an "advanced topics" section. I'll |
Dummy question: why don't you use KeyboardInterrupt instead of a custom SIGINT handler? try: Python SIGINT handler raises a KeyboardInterrupt (the handler is written in C, not in Python) which is safe, whereas writing to sys.stdout doesn't look to be a good idea :-) |
This issue remembers me bpo-3618 (opened 2 years ago): I proposed to use RLock instead of Lock, but RLock was implemented in Python and were too slow. Today, we have RLock implemented in C and it may be possible to use them. Would it solve this issue? -- There are at least two deadlocks, both in _bufferedwriter_flush_unlocked():
Oh, so release the lock around the calls to _bufferedwriter_raw_write() (aorund PyObject_CallMethodObjArgs() in _bufferedwriter_raw_write()) and PyErr_CheckSignals() is not a good idea? Or is it just complex because the buffer object have to be in a consistent state?
If the pending write fails, who gets the error? |
I think it's more complicated. If you use an RLock, you can reenter the
Both :)
Yes, I finally think it's not a good idea. flush() couldn't work |
Ok, so +1 to apply immediatly your patch which "fixes" the deadlock. If someone is motived to make Buffered* classes reentrant, (s)he can remove this exception. io and signal documentation should also be improved to indicate that using buffered I/O in a signal handler may raise a RuntimeError on reentrant call (and give an example to explain the problem?). About the patch: can't you move "&& (self->owner = PyThread_get_thread_ident(), 1) )" in _enter_buffered_busy()? |
Fixed in r86981 (3.2), r86987 (3.1) and r86992 (2.7). Thanks! |
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: