joe, as of at least version 4.2, causes processes it creates to ignore SIGPIPE.
Why is this a bug? Consider yes, a program that repeats lines until terminated by a signal. Type ^K R and try something like:
!yes | head -n 10
Expected behaviour: ten lines containing y are inserted.
Actual behaviour: yes ignores the SIGPIPE sent by head when it's got its ten lines, so yes never stops and simply keeps writing y into the void forever, locking up joe in the process. You now have to open another terminal and kill joe.
Proof of cause: type ^K R and type:
!sh -c 'kill -s PIPE $$; echo IGNORED'
The word IGNORED is inserted into the document.
Um, killing 'yes' instead of 'joe' from that other terminal might be a wiser course of action. Duh. Nonetheless, my report stands.
What environment are you using? I can confirm your second repro does write "IGNORED", and it looks like 3.7 exhibits this behavior as well. But I was unable to get a runaway 'yes' process or any sort of lockup.
Maybe this is shell dependant? JOE disables SIGPIPE, but maybe some shells re-enable it for their sub-processes.
Which shell are you using?
I'm using bash 3.2.57 on Mac OS X 10.11 (El Capitan) which is also the default /bin/sh.
I cannot reproduce this on Linux using the 'yes' command; evidently, Linux 'yes' also checks if writing to stdout succeeds and exits if it doesn't, whereas 'yes' on the Mac just keeps trying forever and depends on SIGPIPE to be terminated.
But the following does reproduce the bug on Linux. Type ^K R and type:
and watch your terminal fill with an endless succession of "broken pipe" errors from
sh, until you manually kill thatshcommand from another terminal.Last edit: Martijn Dekker 2017-10-01
This is now fixed in Mercurial.
The issue was with popen only, shell windows already did the right thing.
Interestingly, python had this exact same problem.
I'm not entirely happy with the solution: I've surrounding the call to popen with signal(SIGPIPE, SIG_DFL) and signal(SIGPIPE_SIG_IGN); This would certainly lead to a race condition if JOE was multi-threaded, and even though it isn't, I'm not 100% sure that there is no issue. Probably better to make my own version of popen that restores the default signal handling after the fork(). Actually I think libc should be doing that.
Well I'm finding that I want the default handling for SIGINT as well, getting closer to rewriting popen().
Yeah, I've implemented the custom popen and now you can hit ^C in a stuck shell command, for example try this:
^K R ! while true; do echo hi 1>&2; echo hi; sleep 1; done
You can now hit ^C to terminate the loop.