Comment by adrianmonk

1 month ago

Tenth Approach: fork() two processes.

Child 1 exec()s the command.

Child 2 does this:

    signal(SIGALRM, alarm_handler);
    alarm(timeout_length);
    pause();
    exit(0);

Start both children, then call wait(), which blocks until any child exits and returns the pid of the child that exited. If it's the command child, then your command finished. If it's the other child, then the timeout expired.

Now that one child has exited, kill() the other child with SIGTERM and reap it by calling wait() again.

All of this assumes you'll only have these two children going, but if you're writing a small exponential backoff command retry utility, that should be OK.

Shouldn't wait() support SIGALRM directly with EINTR?

  • Great question. That was my first idea, but I couldn't get it to work.

    I tried with no signal handler, and the whole process (the parent) gets killed and a generic "Alarm clock" message is printed.

    I tried with a (no-op) signal handler, and wait() keeps waiting.

    "man 2 wait" does seem to say it should work. When I tried it, I installed my handler with signal(). Maybe with sigaction() it would work since "man 7 signal" says it should if I don't pass SA_RESTART.

    • [ Replying to myself since I'm past the edit window. ]

      That was it. I tested it with sigaction(), and when SIGALRM is delivered, wait() returns immediately and sets errno to EINTR.

      But if I pass the SA_RESTART flag to sigaction(), wait() keeps waiting just like when I used signal().

      So yeah, it would work, and you can the same method for timeouts on lots of other system calls too.

      1 reply →