wryun / es-shell

es: a shell with higher-order functions
http://wryun.github.io/es-shell/
Other
307 stars 25 forks source link

`signals = /sigquit` does not ignore `sigquit` like it should #111

Closed jpco closed 2 weeks ago

jpco commented 2 weeks ago

Easy to test with the ^\ sigquit terminal code:

; echo 'es finished, returned:' <=es    # start subshell
; echo $signals 
.sigint /sigquit /sigterm
; ^\                                    # sigquit goes here
es finished, returned: 0                # exited subshell

Compare this with

; echo 'es finished, returned:' <=es    # start subshell
; signals = $signals -sigquit
; echo $signals 
.sigint -sigquit /sigterm
; ^\                                    # sigquit goes here
; ps                                    # did not exit subshell!
    PID TTY          TIME CMD
 316262 pts/0    00:00:00 es
 316681 pts/0    00:00:00 es
 316690 pts/0    00:00:00 ps

Based on my understanding of the man page, these two cases should really behave the same.

This isn't special to sigquit; sigterm does the same, and so does sigint. I think the problem here is the sig_noop handler doesn't really do what it's claimed to.

jpco commented 2 weeks ago

Aha, actually, it's readline that's causing the issue here (and, likely, which has been causing a bunch of small behavioral quirks with signals at the prompt that have been confusing the hell out of me!)

Readline does its own signal handling, as described here https://tiswww.case.edu/php/chet/readline/readline.html#Readline-Signal-Handling. This is fighting with es' signal handling. Weirdly, the docs make it seem like readline's signal handling should just quietly manage its own state and then pass things back to es' handler, but that clearly isn't happening. I assume the right thing is a little more nuanced than just turning off readline signal handling with rl_catch_signals = 0, so I'll have to spend a little time figuring it out.

jpco commented 2 weeks ago

Okay cool, here's what seems to be happening. What seems to be happening is:

  1. es arms its own signal handler
  2. es calls setjmp(slowlabel), sets slow = TRUE and calls readline()
  3. readline arms its own signal handler, saves the es handler
  4. signal comes in, goes to readline handler which does its own stuff
  5. toward the end of the readline handler it re-arms the saved es handler and re-fires the signal
  6. es handler does its stuff and, because slow == TRUE, calls longjmp(slowlabel).
  7. back in input.c, r is set to NULL; SIGCHK() is invoked, but because the signal is sig_noop, no exception is thrown; NULL is returned.
  8. because NULL was returned, nread is set to 0 in fdfill, which causes es to presume EOF has been reached and set fill = eoffill.
  9. yyparse() returns NULL, presumably because input and parsing were interrupted.
  10. %interactive-loop gets a null list from %parse so it calls it again
  11. now because fill == eoffill, the eof exception gets thrown, which causes es to exit.

So the bug is, I think, in step 8; if not for the eoffill setting, things would work correctly. I have a fix in mind.