2

Currently I do:

while [ -d "/proc/$PID"  ]; do
  sleep 1
done

Since I cannot use wait as it is a non-child process.

The problem is that sleep blocks any signal trap handlers (for example for SIGINT or SIGTERM) until it is not sleeping anymore. So each signal will be delayed by 0.5 seconds on average.

I tried to use tail instead to wait for the PID, but it also blocks the trap handler (unless you press CTRL-C from interactive shell). I tried using pwait / pidwait but same problem: they don't exit when a stop signal is received.

So is there a good way to do this, and still be able to handle stop signals?

Maestro
  • 9,046
  • 15
  • 83
  • 116
  • what is the significance of not being able to wait ~0.5 sec for a trap to be triggered? – markp-fuso Apr 16 '23 at 18:27
  • Replace `sleep 1` with `sleep 1 & wait`? – Cyrus Apr 16 '23 at 19:23
  • Possible duplicate of https://stackoverflow.com/q/1058047/6770384. Note [this answer](https://stackoverflow.com/a/28071597/6770384): Check out `pwait`. – Socowi Apr 16 '23 at 20:54
  • @Socowi pwait is only available for FreeBSD and Solaris according to that answer? Im looking for a solution for Debian/Ubuntu/etc. Also it is not really a duplicate because I have the specific requirement that it shouldnt block trap handlers, and the answers there do just that. – Maestro Apr 16 '23 at 21:18
  • If you have recent (3.3.17) `procps` (contains pgrep, pkill, etc.) it is also available on Linux. Might be renamed to `pidwait`, though. From 4.0.0 it's renamed to `pidwait` upstream, so it should become `pidwait` everywhere soon, but right now I have `pwait` on Arch Linux with `procps` 3.3.17, but on Debian 11 it's `pidwait` even though the `procps` version is the same. – Discussian Apr 16 '23 at 21:32
  • It is possible to delay for a time in Bash without using the `sleep` program. One option is to enable the `sleep` builtin command that comes with newer versions of Bash. Another option is to use `read -t TIMEOUT` on something that outputs no data (e.g. a fifo or a subprocess). See [Avoiding busy waiting in bash, without the sleep command](https://unix.stackexchange.com/q/68236/264812). – pjh Apr 16 '23 at 21:43
  • @Discussian I just tried pidwait but it has the same problem as with tail: my trap on SIGTERM is not executed while it is active. The normal wait command does not have this problem as it exits on any signal, but does not support non child processes. I will see if I can find any parameter for pidwait to allow this behaviour, but so far it seems it will not work. – Maestro Apr 16 '23 at 22:59
  • @pjh But do any of them exit on receiving a stop-signal? Because that is my main problem. – Maestro Apr 16 '23 at 23:12
  • As a workaround for your problems with `pwait`/`pidwait` and `trap`, you could try to run it as a background job and `wait` for that job. Alternatively, wrap `pwait`/`pidwait` in another program that does this for you (not sure at the moment, but maybe have a look at `sh -c`, `timeout` or something like that). – Socowi Apr 17 '23 at 00:37
  • @Maestro, a quick test suggests that a SIGINT received while Bash is running a timed `read` on a fifo (e.g. `read -r -t 10 <>fifo`) is processed immediately. I'm not sure if that helps you because I'm not sure how to reproduce your problem. When you write "stop-signal" do you mean SIGSTOP? That signal behaves very differently to SIGINT and SIGTERM. – pjh Apr 17 '23 at 00:39
  • @Socowi That's a very smart idea! I didnt think of that. I will see if it works if I wait on pidwait :) – Maestro Apr 17 '23 at 00:47
  • @pjh I mean SIGTERM and SIGINT, nog SIGSTOP. I also just discovered that read is interrupted correctly. I used the snore() function from this blogpost: https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever which is very similar to your command. My only issue left is that when I read infinitely, it continues reading after executing the trap. So I need some way to cause the read to abort at that point. But maybe it's better to ask this in a seperate question. – Maestro Apr 17 '23 at 00:57
  • 1
    @Maestro, if you manage the fifo yourself (rather than letting the `snore` function do it) you can cause the read to "abort" by writing a line to the fifo. The `read` will read the line and return immediately. – pjh Apr 17 '23 at 01:22
  • can you `sleep 0.1`? – pynexj Apr 17 '23 at 09:08
  • @Socowi Waiting on pidwait works great! If you add that as the answer I can close the question with that. Or shall I answer it myself? – Maestro Apr 17 '23 at 11:20
  • 1
    @pjh I noticed that when I dont abort the read I get very undefined behaviour in my trap handler (freezes on certain lines). And to manage that fifo myself gets to complicated for me, so I'll just use pidwait but thanks for your good suggestions. – Maestro Apr 17 '23 at 11:23
  • See [here](https://stackoverflow.com/questions/9893124/wait-for-arbitrary-process-and-get-its-exit-code-in-linux) for "waiting" for a non-child process. Seems to be tricky, but possible. – user1934428 Apr 17 '23 at 14:00
  • @Maestro glad to hear that it works. You can add your own answer. – Socowi Apr 18 '23 at 13:38

1 Answers1

2

The solution was simple. Instead of calling wait on the PID, I need to call wait on the PID of an executable that is waiting for said PID. This allow the signal traps to be called while waiting.

For example:

pidwait -F "$PIDFILE" & wait $!

or:

tail --pid "$PID" -f /dev/null & wait $!

or even the original loop can be fixed this way by waiting on sleep:

while [ -d "/proc/$PID"  ]; do
  sleep 1 & wait $!
done

This was a bit counter-intuitive, since you are waiting on something that is already waiting, but in hindsight it makes sense.

Maestro
  • 9,046
  • 15
  • 83
  • 116