Thanks for taking care for this! Interesting stuff as well.
2011/6/28 Pádraig Brady <p...@draigbrady.com> > On 27/06/11 21:12, Alan Curry wrote: > > =?UTF-8?Q?P=C3=A1draig?= Brady writes: > >> > >> On 26/06/11 20:20, shay shimony wrote: > >>> all: > >>> timeout 12 sleep 10 > >>> > >>> Note there is a tab before "timeout 12 sleep 10". > >>> Then run at same directory where the file is located "make" and try to > press > >>> CTRL-C. > >>> > >>> Notes: > >>> CTRL-Z works. > >>> When executing timeout without make CTRL-C works. > >>> When executing make without timeout CTRL-C works. > >> > >> Drats, > >> > >> That because SIGINT is sent by the terminal to the foreground group. > >> The issue is that `make` and `timeout` use much the same method > >> to control their jobs. I.E. they create their own process group > >> so they can terminate all sub-processes. > > > > Are you sure? I see no evidence of that. When I run make with the above > > makefile, the processes look like this: > > > > PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND > > 1 1451 1451 1451 6 16407 S 1000 0:06 -zsh > > 1451 16407 16407 1451 6 16407 S 1000 0:00 make > > 16407 16408 16408 1451 6 16407 S 1000 0:00 timeout 60 sleep 30 > > 16408 16409 16408 1451 6 16407 S 1000 0:00 sleep 30 > > > > The first PGID is the login shell. The second PGID is make, which was put > > into its own process group by the shell because the shell has job control > > enabled. The last PGID is timeout, which put itself into a process group. > > make never noticed any of them. > > > > In the source for GNU make 3.82 there are no calls to setpgrp or setpgid > > (unless obfuscated from grep). There is the following comment: > > > > /* A termination signal won't be sent to the entire > > process group, but it means we want to kill the children. */ > > > > That's above the handling of SIGTERM, which iterates over child processes > and > > passes along the SIGTERM to them. > > > > After that is the handling of SIGINT, which doesn't kill child processes > > (unless they're "remote", which is... news to me that make does remote > > things) but just waits for them. > > > > What seems to be happening is that make *doesn't* create a process group, > > therefore assumes that when it gets a SIGINT, its children have already > > gotten it too, and it just waits for them to die. A child that puts > itself > > into a new process group screws this up (as would kill -2 `pidof make`). > > Thanks for the analysis Alan. > Yes you're right I think. > In any case the important point is that timeout sets itself as group > leader, > and is not the foreground group. > > > > > I think the answer is that timeout should put itself into the foreground. > > That way it would get the SIGINT. make wouldn't get it, but wouldn't need > to. > > timeout would exit quickly after SIGINT and make would proceed or abort > > according to the exit code. > > I've a version locally here actually that calls tcsetpgrp() but I > discounted > that as it's not timeout's place to call that I think. > timeout sets itself as group leader so that it can kill everything it > starts, > but it shouldn't need to grab the foreground group as the shell (or make) > may be starting it in the background etc. > > So really `make` should not assume children stay in the same group, > and propagate signals down (like it does for TERM). > It might be appropriate for `make` to call tcsetpgrp() before exec..() > but given remote & parallel jobs etc. signal propagation might be best. > > I've fixed up `timeout` itself not assume children stay in > the same group, and pass signals on, in the attached patch. > > cheers, > Pádraig. >