SIGCHLD trap in a shell

2017-04-25 Thread Vincent Lefevre
I wonder how the following script is supposed to behave.

The POSIX spec seems ambiguous about SIGCHLD trap in a shell. It says:
"[...] a signal specified using a symbolic name, without the SIG prefix,
as listed in the tables of signal names in the  header defined
in XBD Headers"; there, SIGCHLD is listed as "Child process terminated,
stopped, [XSI] or continued". From this spec, I would assume that the
trap would be executed for any child process that terminates, stops, or
continues. But this is not what I observe.

Only bash, ksh93 and zsh document what happens concerning SIGCHLD traps.

Note: For practical reasons, I assume a sleep implementation with
a floating-point number; this is the case with /bin/sleep under
Debian (coreutils). It could be any external command with a similar
behavior.


#!/bin/sh

n=${1:-30}
n=$((n+0))

c=0
trap 'c=$((c+1))' CHLD

for i in `seq $n`; do /bin/sleep 0.1 & done

while true
do
  echo $c
  /bin/sleep 0.1
done


I call this script with:

   

Under Debian unstable:

* With ash, dash, mksh and yash, I get:

1
2
3
4
5
6
[...]

and each time, $c is increased by 1.

The +1 comes from the "/bin/sleep 0.1" of the "while" loop ("true"
and "echo" being built-ins). If I replace them by external commands
(/bin/true and /bin/echo), I get +3 each time.

So, I get +1 only for commands that are not executed in background
(asynchronously).

* With bash and posh, I get:

0
0
0
0
0
0
[...]

i.e. no trap executions at all.

If I add "set -m", then bash behaves like ash, etc., except that it
no longer reacts to Ctrl-C (the Ctrl-C only interrupts the sleep).
Anyway, "set -m" is not supposed to be used in shell scripts (see
rationale for the "set" built-in).

* With ksh93 and zsh, I get:

0
30
30
30
30
30
[...]

(or with a 29 just after the 0 -- this is just a timing reason),
i.e. a trap execution only for asynchronous lists.

This corresponds to the behavior of ksh88 as mentioned in the
rationale for the "set" built-in. Is this the original intent?
In any case, I think that this is the most useful behavior.

-- 
Vincent Lefèvre  - Web: 
100% accessible validated (X)HTML - Blog: 
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)



Re: SIGCHLD trap in a shell

2017-04-25 Thread Joerg Schilling
Vincent Lefevre  wrote:

> I wonder how the following script is supposed to behave.
>
> The POSIX spec seems ambiguous about SIGCHLD trap in a shell. It says:
> "[...] a signal specified using a symbolic name, without the SIG prefix,
> as listed in the tables of signal names in the  header defined
> in XBD Headers"; there, SIGCHLD is listed as "Child process terminated,
> stopped, [XSI] or continued". From this spec, I would assume that the
> trap would be executed for any child process that terminates, stops, or
> continues. But this is not what I observe.
>
> Only bash, ksh93 and zsh document what happens concerning SIGCHLD traps.

Do you believe every shell should document SIGCHLD?
Wouldn't it be sufficient if SIGCHLD is handled the same way as other signals?


> Note: For practical reasons, I assume a sleep implementation with
> a floating-point number; this is the case with /bin/sleep under
> Debian (coreutils). It could be any external command with a similar
> behavior.
>
> 
> #!/bin/sh
>
> n=${1:-30}
> n=$((n+0))
>
> c=0
> trap 'c=$((c+1))' CHLD
>
> for i in `seq $n`; do /bin/sleep 0.1 & done
>
> while true
> do
>   echo $c
>   /bin/sleep 0.1
> done
> 
>
> I call this script with:
>
>
>
> Under Debian unstable:
>
> * With ash, dash, mksh and yash, I get:
>
> 1
> 2
> 3
> 4
> 5
> 6
> [...]
>
> and each time, $c is increased by 1.

Well, /bin/sleep 0.1 is definitely not portable, I used /bin/sleep 1
instead...

I get this behavor with:

dash, bosh (the POSIXyfied Bourne Shell), mksh and yash.


zsh delivers:

0
9
30
30
30
30
^C

ksh88 delivers:
0
0
0
0
^C

ksh93 delivers:
0
1
2
2
2
2
2
^C

bash delivers:
0 
0 
0 
0 
^C 

zsh delivers:
0
7
30
30
^C

which looks similar to what ksh93 does.

> * With bash and posh, I get:
>
> 0
> 0
> [...]
>
> i.e. no trap executions at all.
>
> If I add "set -m", then bash behaves like ash, etc., except that it

bash -m script does not work for me...


>From my interpretation, only:

dash, bosh (the POSIXyfied Bourne Shell), mksh and yash

and probably zsh

work as expected.

Jörg

-- 
 EMail:jo...@schily.net(home) Jörg Schilling D-13353 Berlin
joerg.schill...@fokus.fraunhofer.de (work) Blog: http://schily.blogspot.com/
 URL: http://cdrecord.org/private/ http://sf.net/projects/schilytools/files/'



Re: SIGCHLD trap in a shell

2017-04-25 Thread Joerg Schilling
Joerg Schilling  wrote:

> From my interpretation, only:
>
>   dash, bosh (the POSIXyfied Bourne Shell), mksh and yash
>
> and probably zsh
>
> work as expected.

BTW: I should have mentioned that this test cannot be done with the original 
Bourne Shell, as there, the script would need to contain:

trap 'c=`expr $c + 1`' CHLD

and this would cause a SIGCHLD storm as a result of the end of the "expr" 
process.

Depsite that fact, the original Bourne Shell could also be seen as compliant.

Jörg

-- 
 EMail:jo...@schily.net(home) Jörg Schilling D-13353 Berlin
joerg.schill...@fokus.fraunhofer.de (work) Blog: http://schily.blogspot.com/
 URL: http://cdrecord.org/private/ http://sf.net/projects/schilytools/files/'



Re: SIGCHLD trap in a shell

2017-04-25 Thread Jilles Tjoelker
On Tue, Apr 25, 2017 at 03:21:24PM +0200, Vincent Lefevre wrote:
> I wonder how the following script is supposed to behave.

> The POSIX spec seems ambiguous about SIGCHLD trap in a shell. It says:
> "[...] a signal specified using a symbolic name, without the SIG prefix,
> as listed in the tables of signal names in the  header defined
> in XBD Headers"; there, SIGCHLD is listed as "Child process terminated,
> stopped, [XSI] or continued". From this spec, I would assume that the
> trap would be executed for any child process that terminates, stops, or
> continues. But this is not what I observe.

I would not assume that. Since trap actions are not executed while
waiting for a foreground process to exit, it is likely that multiple
instances of SIGCHLD will be coalesced into a single trap action
execution.

Also, SIGCHLD instances may be lost if waitpid() or similar calls
implicitly accept them. This is most likely if the shell calls waitpid()
with SIGCHLD masked but might also happen in a race otherwise.

> Only bash, ksh93 and zsh document what happens concerning SIGCHLD traps.

> Note: For practical reasons, I assume a sleep implementation with
> a floating-point number; this is the case with /bin/sleep under
> Debian (coreutils). It could be any external command with a similar
> behavior.

> 
> #!/bin/sh
> 
> n=${1:-30}
> n=$((n+0))
> 
> c=0
> trap 'c=$((c+1))' CHLD
> 
> for i in `seq $n`; do /bin/sleep 0.1 & done
> 
> while true
> do
>   echo $c
>   /bin/sleep 0.1
> done
> 

> I call this script with:

>

> Under Debian unstable:

> * With ash, dash, mksh and yash, I get:

> 1
> 2
> 3
> 4
> 5
> 6
> [...]

> and each time, $c is increased by 1.

> The +1 comes from the "/bin/sleep 0.1" of the "while" loop ("true"
> and "echo" being built-ins). If I replace them by external commands
> (/bin/true and /bin/echo), I get +3 each time.

> So, I get +1 only for commands that are not executed in background
> (asynchronously).

You can get the SIGCHLD trap action executed for background jobs by
doing slow things that do not fork, such as invoking the read utility.

FreeBSD sh behaves like this as well, with the addition of a check that
prevents nested SIGCHLD traps.

> * With bash and posh, I get:

> 0
> 0
> 0
> 0
> 0
> 0
> [...]

> i.e. no trap executions at all.

> If I add "set -m", then bash behaves like ash, etc., except that it
> no longer reacts to Ctrl-C (the Ctrl-C only interrupts the sleep).
> Anyway, "set -m" is not supposed to be used in shell scripts (see
> rationale for the "set" built-in).

"set -m" can be used usefully in scripts to put jobs into their own
process groups, like
  set -m; something & set +m

This example turns -m mode off immediately, since handling suspension of
foreground jobs is usually undesirable in a script.

Whether this works well depends on the shell implementation.

In any case it seems strange to tie SIGCHLD behaviour to "set -m".

> * With ksh93 and zsh, I get:

> 0
> 30
> 30
> 30
> 30
> 30
> [...]

> (or with a 29 just after the 0 -- this is just a timing reason),
> i.e. a trap execution only for asynchronous lists.

> This corresponds to the behavior of ksh88 as mentioned in the
> rationale for the "set" built-in. Is this the original intent?
> In any case, I think that this is the most useful behavior.

That seems more useful, but there is no text in POSIX that says to
implement it that way.

-- 
Jilles Tjoelker



Re: SIGCHLD trap in a shell

2017-04-25 Thread Robert Elz
Date:Tue, 25 Apr 2017 15:21:24 +0200
From:Vincent Lefevre 
Message-ID:  <20170425132124.ga7...@cventin.lip.ens-lyon.fr>

I see Jillles has said much the same thing while I was preparing
this, but ...

  | I wonder how the following script is supposed to behave.

I wonder more if it has any expected behaviour.

  | I would assume that the
  | trap would be executed for any child process that terminates, stops, or
  | continues. But this is not what I observe.

I suspect here that your observations are (partly) incorrect.

  | 
  | #!/bin/sh
  | 
  | n=${1:-30}
  | n=$((n+0))
  | 
  | c=0
  | trap 'c=$((c+1))' CHLD
  | 
  | for i in `seq $n`; do /bin/sleep 0.1 & done
  | 
  | while true
  | do
  |   echo $c
  |   /bin/sleep 0.1
  | done
  | 
  | 
  | I call this script with:
  | 
  |
  | 
  | Under Debian unstable:

  | * With ash, dash, mksh and yash, I get:

With the NetBSD sh (which is an ash derivative) that is what I see too.

  | So, I get +1 only for commands that are not executed in background
  | (asynchronously).

No, that does not fit with your observations.  If that were true,m the first
output value would be 0, as the exct is done before the first foreground
sleep.

What is happening here is that all the background processes are created
at about the same time, all run for very little time, and all will terminate
at essentially the same time.  There's nothing that requires signals to be
queued anywhere (though they may be).

What I ovserve is that all the background sleep processes have finished
before the shell gets to the while true loop, and exactly one SIGCHLD trap
gets executed by the shell (setting c to 1) and then after that, it
all goes as you observe.

Of course, all this is horribly racy, and other outputs might also occur,

  | * With bash and posh, I get:

  | i.e. no trap executions at all.

That is also not unreasonable - systems are permitted to rescind SIGCHLD
if a child process is waited upon - and it is entirely reasonable to block
SIGCHLD (and perhaps other signals) while in the wait handler, so if no
children die at a time that the shell happens to have SIGCHLD unblocked
(which might not be a very long interval) it is possile for no CHLD signals
will ever be delivered.

kre





Re: SIGCHLD trap in a shell

2017-04-25 Thread Vincent Lefevre
On 2017-04-25 23:49:56 +0200, Jilles Tjoelker wrote:
> On Tue, Apr 25, 2017 at 03:21:24PM +0200, Vincent Lefevre wrote:
> > I wonder how the following script is supposed to behave.
> 
> > The POSIX spec seems ambiguous about SIGCHLD trap in a shell. It says:
> > "[...] a signal specified using a symbolic name, without the SIG prefix,
> > as listed in the tables of signal names in the  header defined
> > in XBD Headers"; there, SIGCHLD is listed as "Child process terminated,
> > stopped, [XSI] or continued". From this spec, I would assume that the
> > trap would be executed for any child process that terminates, stops, or
> > continues. But this is not what I observe.
> 
> I would not assume that. Since trap actions are not executed while
> waiting for a foreground process to exit, it is likely that multiple
> instances of SIGCHLD will be coalesced into a single trap action
> execution.

IMHO, this would be a bug. A shell should be able to know when all
its children have terminated. IMHO, all the pending traps should
be executed before the next command.

-- 
Vincent Lefèvre  - Web: 
100% accessible validated (X)HTML - Blog: 
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)



Re: SIGCHLD trap in a shell

2017-04-25 Thread Vincent Lefevre
On 2017-04-25 16:00:54 +0200, Joerg Schilling wrote:
> Vincent Lefevre  wrote:
> > Only bash, ksh93 and zsh document what happens concerning SIGCHLD traps.
> 
> Do you believe every shell should document SIGCHLD?
> Wouldn't it be sufficient if SIGCHLD is handled the same way as
> other signals?

Yes, this would be better.

> Well, /bin/sleep 0.1 is definitely not portable, I used /bin/sleep 1
> instead...

I mentioned the portability issue. You could replace it with
a different command that takes a fraction of time. I used 0.1
instead of 1 because I didn't want to wait for 30 seconds (this
was also an attempt to get different behaviors for the first
values).

> ksh93 delivers:
> 0
> 1
> 2
> 2
> 2
> 2
> 2
> ^C

Are you sure? Is there any reason why I don't get the same behavior
with ksh93?

> bash delivers:
> 0 
> 0 
> 0 
> 0 
> ^C 
> 
> zsh delivers:
> 0
> 7
> 30
> 30
> ^C
> 
> which looks similar to what ksh93 does.

Not with the above result you got.

-- 
Vincent Lefèvre  - Web: 
100% accessible validated (X)HTML - Blog: 
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)



Re: SIGCHLD trap in a shell

2017-04-25 Thread Vincent Lefevre
On 2017-04-26 06:12:25 +0700, Robert Elz wrote:
> Date:Tue, 25 Apr 2017 15:21:24 +0200
> From:Vincent Lefevre 
> Message-ID:  <20170425132124.ga7...@cventin.lip.ens-lyon.fr>
> 
> I see Jillles has said much the same thing while I was preparing
> this, but ...
> 
>   | I wonder how the following script is supposed to behave.
> 
> I wonder more if it has any expected behaviour.
> 
>   | I would assume that the
>   | trap would be executed for any child process that terminates, stops, or
>   | continues. But this is not what I observe.
> 
> I suspect here that your observations are (partly) incorrect.

Nope...

>   | 
>   | #!/bin/sh
>   | 
>   | n=${1:-30}
>   | n=$((n+0))
>   | 
>   | c=0
>   | trap 'c=$((c+1))' CHLD
>   | 
>   | for i in `seq $n`; do /bin/sleep 0.1 & done
>   | 
>   | while true
>   | do
>   |   echo $c
>   |   /bin/sleep 0.1
>   | done
>   | 
>   | 
>   | I call this script with:
>   | 
>   |
>   | 
>   | Under Debian unstable:
> 
>   | * With ash, dash, mksh and yash, I get:
> 
> With the NetBSD sh (which is an ash derivative) that is what I see too.
> 
>   | So, I get +1 only for commands that are not executed in background
>   | (asynchronously).
> 
> No, that does not fit with your observations. If that were true,m
> the first output value would be 0, as the exct is done before the
> first foreground sleep.

Wrong. The first trap comes from the seq command. This can be seen by
replacing `seq $n` with a fixed list: the first output value becomes 0.

> What is happening here is that all the background processes are created
> at about the same time, all run for very little time, and all will terminate
> at essentially the same time.  There's nothing that requires signals to be
> queued anywhere (though they may be).

No, replacing "/bin/sleep 0.1 &" with ":;" yields the same behavior.
So, the background processes don't trigger the trap (see also my
remark above, where one gets 0 even though there are background
processes).

-- 
Vincent Lefèvre  - Web: 
100% accessible validated (X)HTML - Blog: 
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)



Re: SIGCHLD trap in a shell

2017-04-25 Thread Chet Ramey
On 4/25/17 9:15 PM, Vincent Lefevre wrote:
> On 2017-04-25 23:49:56 +0200, Jilles Tjoelker wrote:
>> On Tue, Apr 25, 2017 at 03:21:24PM +0200, Vincent Lefevre wrote:
>>> I wonder how the following script is supposed to behave.
>>
>>> The POSIX spec seems ambiguous about SIGCHLD trap in a shell. It says:
>>> "[...] a signal specified using a symbolic name, without the SIG prefix,
>>> as listed in the tables of signal names in the  header defined
>>> in XBD Headers"; there, SIGCHLD is listed as "Child process terminated,
>>> stopped, [XSI] or continued". From this spec, I would assume that the
>>> trap would be executed for any child process that terminates, stops, or
>>> continues. But this is not what I observe.
>>
>> I would not assume that. Since trap actions are not executed while
>> waiting for a foreground process to exit, it is likely that multiple
>> instances of SIGCHLD will be coalesced into a single trap action
>> execution.
> 
> IMHO, this would be a bug. A shell should be able to know when all
> its children have terminated. IMHO, all the pending traps should
> be executed before the next command.

There is no requirement that a trap be executed each time a particular
signal is generated.  There is no requirement that the system keep a
count of instances of a particular pending signal; only that the signal
is pending.  SIGCHLD isn't different from any other signal in this
regard.

-- 
``The lyf so short, the craft so long to lerne.'' - Chaucer
 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/