1

I was looking at this stack exchange question: how to call a function automatically at regular intervals?

And I tried running the code in the first answer

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

void timer_handler (int signum)
{
 static int count = 0;
 printf ("timer expired %d times\n", ++count);
}

int main ()
{
 struct sigaction sa;
 struct itimerval timer;

 /* Install timer_handler as the signal handler for SIGVTALRM. */
 memset (&sa, 0, sizeof (sa));
 sa.sa_handler = &timer_handler;
 sigaction (SIGVTALRM, &sa, NULL);

 /* Configure the timer to expire after 250 msec... */
 timer.it_value.tv_sec = 0;
 timer.it_value.tv_usec = 250000;
 /* ... and every 250 msec after that. */
 timer.it_interval.tv_sec = 0;
 timer.it_interval.tv_usec = 250000;
 /* Start a virtual timer. It counts down whenever this process is
   executing. */
 setitimer (ITIMER_REAL, &timer, NULL);

 /* Do busy work. */
 while (1);
}

I don't understand what it is doing. It appears to print "Alarm Clock" after 2500 milliseconds, but I don't understand how that's possible since there is no print statement to that effect. How do I get it to increment the counter every 2500 milliseconds like its supposed to?

Aperson123
  • 129
  • 6
  • It should print `timer expired 1 times`, `timer expired 2 times` etc., each one after 2500ms. Are you sure the progam run is the program you have compiled? – Jabberwocky Feb 18 '19 at 15:52
  • The code is flawed if the timer ever generates a `SIGVTALARM` signal because the handler for it calls a function (`printf`) that is not async-signal-safe. – John Bollinger Feb 18 '19 at 15:52
  • @JohnBollinger ...even if the main program does an infinite loop doing nothing like in the OP's code? – Jabberwocky Feb 18 '19 at 15:54
  • What is the OS/environment? And this `while (1);` probably will load the CPU to 100%. – i486 Feb 18 '19 at 15:55
  • @i486 Yeah, `while(1) pause();` would've been more gentle to the CPU. – Petr Skocik Feb 18 '19 at 15:57
  • 1
    Yes, @Jabberwocky. Undefined behavior results from a signal handler calling a function that is not `async-signal-safe`. Period. You may happen to get away with it, especially in a case like this one, but you cannot rely on it, and you should not present it as a model that people might try to emulate. – John Bollinger Feb 18 '19 at 15:58
  • 1
    See [How to avoid calling `printf()` in a signal handler](https://stackoverflow.com/questions/16891019/) – Jonathan Leffler Feb 18 '19 at 16:23
  • 1
    @JohnBollinger I thought there was a provision for allowing async-signalunsafe code if it interrupted code that was async-signal safe but I looked into POSIX and it doesn't seem to be allowed (I know there's at least one threading framework that used this assumption to optimize out certain sigprocmask calls). Anyway, I think it ought to be safe based on how signals work. – Petr Skocik Feb 18 '19 at 16:28
  • So POSIX specifically allows long-jumping (http://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html) out of a handler iff the handler interrupts async-signal-safe code, and it doesn't say anything about prohibiting async-signal-unsafe code after such a longjmp, so I think POSIX must be virtually guaranteeing the safety of interrupting async-signal-safe code with a async-signal-unsafe handler as a consequence of this. – Petr Skocik Feb 18 '19 at 16:43
  • @PSkocik, the doc you linked says "if it is invoked from a signal handler which interrupted a non-async-signal-safe function or equivalent [...], the behavior of any subsequent call to a non-async-signal-safe function or equivalent is undefined." It of course does not use the term "prohibit", but effectively, yes, it *does* prohibit async-signal-unsafe code after a signal handler `longjmp`s. – John Bollinger Feb 18 '19 at 16:49
  • And that's in any case supplemental to "the behavior is undefined if the signal handler [...] calls any function defined in this standard other than one of the functions listed in the following table [of async-signal-safe functions]." – John Bollinger Feb 18 '19 at 16:54
  • @JohnBollinger But only if the longjmp interrupted a **non**-async-signal-safe function. – Petr Skocik Feb 18 '19 at 16:55
  • 1
    @PSkocik, that `longjmp`, despite being async-signal-safe, has additional constraints on its use by signal handlers does not conflict with the general provision I quoted in my previous comment. And since you seem to prefer thinking in terms of expected behavior rather than formal definedness, I'll observe that there can be reasons entirely independent of the interrupted context for non async-signal-safe functions to misbehave when called from a signal handler. For example, they may require more stack than is provided to signal handlers. – John Bollinger Feb 18 '19 at 17:11

1 Answers1

6

ITIMER_REAL sends SIGALRM not SIGVTALRM. Change the signal and it'll work.

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

void timer_handler (int signum)
{
 static int count = 0;
 printf ("timer expired %d times\n", ++count);
}

int main ()
{
 struct sigaction sa;
 struct itimerval timer;

 /* Install timer_handler as the signal handler for SIGVTALRM. */
 memset (&sa, 0, sizeof (sa));
 sa.sa_handler = &timer_handler;
 sigaction (SIGALRM, &sa, NULL);

 /* Configure the timer to expire after 250 msec... */
 timer.it_value.tv_sec = 0;
 timer.it_value.tv_usec = 250000;
 /* ... and every 250 msec after that. */
 timer.it_interval.tv_sec = 0;
 timer.it_interval.tv_usec = 250000;
 /* Start a virtual timer. It counts down whenever this process is
   executing. */
 setitimer (ITIMER_REAL, &timer, NULL);

 /* Do busy work. */
 while (1);
}

(Generally, it's a bad idea to printf in a signal handler since printf isn't async-signal safe, but in your case it shouldn't be dangerous, because you're interrupting regular-context code that is async-signal safe (namely the busy loop). POSIX doesn't appear to guarantee this special exception, though, so to be perfectly safe, you should refrain from making any async-signal unsafe calls in signal handlers and replace the printf with a write(1, ...).)

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142