Signal Handler to stop Timer in C

13,188

Solution 1

Just set it_interval to zero, and you'll get a one-shot timer. You don't need to do anything with it in your handler.

For instance, with this:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>

#define INTERVAL 2              // number of seconds to go off

void TimerStop(int signum) {
    printf("Timer ran out! Stopping timer\n");
}

void TimerSet(int interval) {
    printf("starting timer\n");
    struct itimerval it_val;

    it_val.it_value.tv_sec = interval;
    it_val.it_value.tv_usec = 0;
    it_val.it_interval.tv_sec = 0;
    it_val.it_interval.tv_usec = 0;

    if (signal(SIGALRM, TimerStop) == SIG_ERR) {
        perror("Unable to catch SIGALRM");
        exit(1);
    }
    if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) {
        perror("error calling setitimer()");
        exit(1);
    }
}

int main(int argc, char *argv[]) {

    TimerSet(INTERVAL);

    while (1) {
        // do stuff
    }
    return 0;
}

the message "Timer ran out! Stopping timer" will appear only once, and your timer will stop without you doing anything.

Note that you need to fill in the tv_usec members of your struct itimerval, which your current code does not do. If you don't, it_interval is highly unlikely to be zero, and your timer will never stop.

printf(), along with the other standard IO functions, is not really safe to call from a signal handler, although in this particular case it won't cause you any problems, since the main code is just sitting in a loop and not doing anything.

Also, presume you're calling signal() on purpose - sigaction() is the recommended way for setting handlers. setitimer() is also obsolete, now, and timer_settime() is recommended.

Solution 2

According to manual:

   Timers  decrement  from  it_value  to zero, generate a signal, and reset to
   it_interval.  A timer which is set to zero (it_value is zero or  the  timer
   expires and it_interval is zero) stops.

   Both  tv_sec  and  tv_usec are significant in determining the duration of a
   timer.

So timer can be set to run only once if interval is set to zero before setitimer() call (thanks to Duck's comment).

Share:
13,188
Olivier
Author by

Olivier

Updated on August 21, 2022

Comments

  • Olivier
    Olivier over 1 year

    I am trying to have a signal handler stop a timer without exiting my program. How should I go about. I want StopTimer to handle the signal to stop the timer

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <sys/time.h>
    #include <signal.h>
    #include <unistd.h>
    
    #define INTERVAL 2      // number of seconds to go off
    
    int main(int argc, char* argv[]) {
    
    TimerSet(INTERVAL);
    
    while(1)
    {
        // do stuff
    }
    return 0;
    }
    
    void TimerSet(int interval)
    {
    printf("starting timer\n");
    struct itimerval it_val;
    
    // interval value
    it_val.it_value.tv_sec = interval;
    it_val.it_interval = it_val.it_value;
    
    // on SIGALRM, close window
    if (signal(SIGALRM, TimerStop) == SIG_ERR)
    {
        perror("Unable to catch SIGALRM");
        exit(1);
    }
    
    // set interval timer, returns SIGALRM on expiration
    if (setitimer(ITIMER_REAL, &it_val, NULL) == -1)
    {
        perror("error calling setitimer()");
        exit(1);
    }
    }
    
    void TimerStop(int signum)
    {
    printf("Timer ran out! Stopping timer\n");
    exit(signum);
    }
    

    I tried to set the setitimer interval to 0, but I am not sure how to use the same timer within the TimerStop signal handler function

  • Olivier
    Olivier over 10 years
    I am getting this error: error calling setitimer(): Invalid argument. When trying to stop the timer. ALso when I set the interval to 0, it does not stop the timer
  • Duck
    Duck over 10 years
    setitimer isn't safe to call from w/i a signal handler. I think OP just wants to shut down the timer. All he needs to do is zero out the interval values when he creates the timer.
  • Michael
    Michael over 10 years
    @Olihoops I fixed my answer. As mentioned in man (and as Duck recommends) - it also possible to set interval to zero before starting timer.
  • Olivier
    Olivier over 10 years
    I want to have my timer run again at some time later in the program. I tried making the interval global and have a function change the interval so that my timer starts again. However, the timer only runs once now.
  • Olivier
    Olivier over 10 years
    I want to have my timer run later at some point in my program. I made it_val global, and have a function that changes the interval. However, the timer stops forever. How can I make the timer go ON and OFF when I want it to? setitimer can only be run once.
  • Crowman
    Crowman over 10 years
    What makes you think setitimer() can only be run once? You make it go off by calling setitimer() with everything zero, and make it go on by calling it with something else.
  • Olivier
    Olivier over 10 years
    You are correct, I must have done something wrong earlier. THis works. Thank you