Daniel_Bogorad
Daniel_Bogorad

Reputation: 13

Using signals in C, how to stop and continue a program when timer ends

I'm developing a program that runs on a Raspberry-Pi (linux), that gets data from GPS modules and processes it.

Right now, the program is centered around a while(1) loop that takes the GPS data and does stuff with it, (data is streamed at the frequency of the GPS module).

However, I know while(1) is not a very power efficient solution, (This RPi is going to sit on a drone later).

I'd like to set up a timer, and only when the timer ends, will the program take GPS data. Ideally, the program would be completely stopped until the timer ends, so that the CPU is not wasting time/energy on it.

How can I do the above mentioned, if SIGSTOP signal is not allowed in sigaction() calls ?

void timer_handler(int signum){
    /* what can I do to make a program stop and coninue,
       so that no CPU cycles are devoted to this program until
       the timer is elapsed ? (SIGSTOP is not allowed) */
}

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

    struct sigaction sigAct;
    struct itimerval timer;
    sa.sa_handler = &timer_handler;

    // SIGALRM is only a place holder here.. 
    sigaction(SIGALRM, &sigAct, NULL);

    /* Here would be some timer.it_value, timer.it_interval stuff,
    setting it to some arbitrary time */

    setitimer(ITIMER_REAL, &timer, NULL);

    /* Main program loop here */
    while(1){
        // process GPS data.
        // instead of while(1), I'd like this loop to run only when
        // the timer ends.
    }

}

Upvotes: 0

Views: 2548

Answers (1)

Crowman
Crowman

Reputation: 25946

In your loop, just call pause(). It'll block and only return when a signal is received, so then you can run your code, loop again, and repeat. You need a signal handler to stop SIGALRM from terminating your program, but it doesn't have to do anything, you can just leave the function body empty.

For instance:

#define _POSIX_C_SOURCE 200809L

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

void timer_handler(int signum)
{
   (void) signum;       /*  Avoids warning for unused argument  */
}

int main(void)
{
    struct sigaction sa;
    sa.sa_handler = timer_handler;
    sa.sa_mask = 0;
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    struct itimerval timer;
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);

    while ( 1 ) {
        pause();
        printf("Timer expired - get GPS data.\n");
    }

    return 0;
}

yields output:

paul@horus:~/src/sandbox$ ./alarm
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
^C
paul@horus:~/src/sandbox$

It's a fairly crude solution. If there's any possibility that the code may sometimes take longer to run than the timer interval, then things become unreliable and you may sometimes skip a signal. You may or may not care about this. For a more sophisticated approach, you can block receipt of SIGALRM and then call sigsuspend() with a signal mask that unblocks it, safe in the knowledge that the unblocking and the waiting for the signal will be an atomic operation. Here's an example of that approach:

#define _POSIX_C_SOURCE 200809L

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

void timer_handler(int signum)
{
    static const char msg[] = "Handler was called.\n";
    write(STDIN_FILENO, msg, sizeof(msg) - 1);
    (void) signum;
}

int main(void)
{
    struct sigaction sa;
    sa.sa_handler = timer_handler;
    sa.sa_mask = 0;
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    struct itimerval timer;
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);

    sigset_t old_mask, block_mask;
    sigemptyset(&block_mask);
    sigaddset(&block_mask, SIGALRM);
    sigprocmask(SIG_BLOCK, &block_mask, &old_mask);

    sleep(3);   /*  To demonstrate signal handler won't be
                    called until sigsuspend() is called, timer
                    is firing every second while we're sleeping  */

    while ( 1 ) {
        sigsuspend(&old_mask);
        printf("Timer expired - get GPS data.\n");
    }

    return 0;
}

with output:

paul@horus:~/src/sandbox$ ./alarm2
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
^C
paul@horus:~/src/sandbox$

Error checking has been omitted for brevity in the above examples, but your code should of course include it for every system call.

Upvotes: 2

Related Questions