Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Z shell signal handling

On Mon, May 16, 2005 at 11:46:49AM +0100, Peter Stephenson wrote:
> Vincent Stemen wrote:
> > # --------- <test script> -----------
> > sigterm1()
> > {
> >     trap 'echo "-- sigterm2 --"' TERM
> >     echo "sigterm1(): sending SIGTERM"
> >     kill -TERM $$
> >     trap sigterm1 TERM
> >     sleep 1
> > }
> > 
> > trap sigterm1 TERM
> > 
> > echo
> > echo "main: sending SIGTERM"
> > kill -TERM $$
> > echo "main: sending SIGTERM"
> > kill -TERM $$
> > # --------- </test script> -----------
> >
> > Here is the output of zsh and bash.
> > 
> > <zsh>
> > main: sending SIGTERM
> > sigterm1(): sending SIGTERM
> > sigterm1(): sending SIGTERM
> > sigterm1(): sending SIGTERM
> > sigterm1(): sending SIGTERM
> > ... continues forever
> > </zsh>
> Yes, I can see this, but this isn't the same example I was looking at
> when I saw different behaviour from you.

Yes, I think that is because we were discussing localtraps earlier as
well, but I thought it was better to simplify the test case as much as
possible and set any localtrap issues aside for now and focus on the
fundamental signal behavior that can be directly compared to other

> What I think is happening here is that signals *are* being queued.  Then
> the SIGTERM is delivered after sigterm1 has returned, at which point
> sigterm1 has been reinstalled as the signal handler, so gets called
> again ad infinitum.

Yes.  That is the problem basically as I see it.

> In this case bash and zsh are actually trying a bit harder to do the
> right thing, at least as far as safety of the execution environment is
> concerned.  Not queueing signals can cause significant reentrancy
> problems.  However, it may be possible to unqueue signals temporarily at
> some point.  That's still not trivial; the code's currently not written
> to allow that, since as I said before the queue/unqueues are all nested
> and you need to have some clue about the environment to decide whether
> temporarily unqueuing is safe.  Possibly some queue/unqueue pair can be
> moved deeper into the code, leaving some points at which signals will be
> handled, but that's not trivial either.

I have a proposal as to the way I think the signals should be handled,
although, as you say, it may not be trivial.  First, let me provide
one more example to elaborate on the current bahavior comparing zsh to
sh.  There is a problem with the way sh does it also, which I will
describe below.

# --- <test script> ---
  echo "-- signal() --"
  echo "sleeping"
  sleep 1
  echo "sleeping again"
  sleep 1

trap signal INT
echo "main: sending SIGINT"
kill -INT $$
# --- </test script> ---

To test, I hit ^C several times while it is in signal().

main: sending SIGINT
-- signal() --
^C^C^C^Csleeping again
^C-- signal() --
sleeping again

main: sending SIGINT
-- signal() --
^C-- signal() --
^C-- signal() --
^C-- signal() --
sleeping again
sleeping again
sleeping again
sleeping again

zsh seems to queue one signal and re-calls the handler once.  All
additional signals are ignored.  I am not sure I see the benefit of

sh would appear to queue many signals, but actually it is not having
to queue them because it calls the handler immediately on each one.
So, I don't know if it even has any kind of queuing mechanism.

There are a couple potential problems I can see with the sh approach,
that I am guessing zsh and bash were trying to fix.
  1. It enters the handler with the signal still enabled, so the programmer
     needs to know to immediately use trap to disable the signal, once
     in the handler, if he does not want get hit with another signal
     before exiting the handler.

  2. Even if you disable the signal in the handler, there might be the
     remote possibility of a race case where it could catch another
     signal before getting it disabled.  If you are talking about
     dealing with interrupts at the kernel level, I would certainly
     consider this an issue.  But with signals in shell script, I
     would generally consider the chance of this very low or near
     zero.  I don't know of many things that would generate multiple
     signals that close together.  The user certainly is not going to
     be able to hit ^C or type the kill command that fast.  Maybe if
     you had multiple other processes sending signals to the same
     process and two of them happen to hit at near the same time.

     This leaves problem 1 as the primary issue.

Here is my proposal of the way I think it should be handled, which is
very simple conceptually.  Although I understand, Peter, that
implementing it might not be so simple.  I will, of course, leave that
up to you or whoever might have the time, ability, and hopefully
willingness to work on it to determine.


When a signal is caught, enter the handler with the signal
automatically disabled the way bash does.  Then allow the programmer
to re-enable the signal with a trap statement inside the handler if
needed.  If another signal is caught after that, process it
immediately like sh does, rather than waiting for the handler to

This way, there would be no potential race cases, programmers won't
get themselves into trouble by not thinking of disabling the signal in
the handler, and our hands won't be tied when we need to do more
sophisticated signal handling.

It seems to me that would solve all the problems and possibly
eliminate the need to have complex signal queuing code.  I not sure
you would need to do any queuing at all since all signals would be
processed immediately.

Vincent Stemen
Avoid the VeriSign/Network Solutions domain registration trap!
Read how Network Solutions (NSI) was involved in stealing our domain name.

Messages sorted by: Reverse Date, Date, Thread, Author