Skip to content
Advertisement

SIGALRM, interval timers and problems with sleep()

I am working with porting one of our embedded microcontroller libraries to linux and writing a python wrapper around it.

One of my low level modules depends on a callback that gets called every 10ms. This callback counts up software timers that each have callbacks of their own. These timers are used all over our library and while they don’t have to be 100% accurate, they are expected to continue ticking and hopefully hit an accuracy +/- 10%. This module mimics a standard timer interrupt within a microcontroller.

I have an existing implementation using setitimer and SIGALRM handlers, which works wonderfully. Here is some of my code:

void halCounterInit(void)
{
    struct sigaction alrm_action;
    alrm_action.sa_handler = timerInterrupt;
    alrm_action.sa_flags = SA_RESTART;
    sigaction(SIGALRM, &alrm_action, NULL);
}

void halCounterEnable(void)
{
    mytime.it_interval.tv_sec = 0;
    mytime.it_interval.tv_usec = 10000; //10ms
    mytime.it_value.tv_sec = 0;
    mytime.it_value.tv_usec = 10000; //10ms
    setitimer(ITIMER_REAL, &mytime, NULL);
}

void halCounterDisable(void)
{
    mytime.it_interval.tv_sec = 0;
    mytime.it_interval.tv_usec = 0; 
    mytime.it_value.tv_sec = 0;
    mytime.it_value.tv_usec = 0; 
    setitimer(ITIMER_REAL, &mytime, NULL);
}

void halCounterRegisterInterruptHandler(InterruptHandlerCallback cb, void *params)
{
    myInterruptHandler = cb;
    myParams = params;
}

void timerInterrupt(int signum)
{
    if (myInterruptHandler != NULL)
    {
        myInterruptHandler(myParams);
    }
}

While testing some of this code in conjunction with other code, I noticed that calls to sleep() kick out early when the SIGALRM signal is generated. I am interfacing with gevent (based on libevent) in python using ctypes in order to create a new layer above my C library. I am concerned that this SIGALRM issue will cause difficult to find bugs within python.

I attempted to rewrite my simple counter using a pthread and a blocking select() call, but it seems like the select() call exits out early and my timers become horrifically inaccurate.

  1. Is there anyway to get setitimer to generate a different signal that I can catch, instead of sigalrm?
  2. Is there another way to do this without using signals? I’ve tried nanosleep, select and regular sleep in another thread, but that doesn’t work very well.

Any help is appreciated. Ideally, this would work for both OSX and linux, but Linux is the only hard requirement.

Edit:

My separate thread code using select() or nanosleep(). Both are inaccurate. The select implementation is commented out. MyTime is configured correctly in a init function. This is the thread function:

while (TRUE)
{
    pthread_mutex_lock(&Lock);
    //we are paused
    while(!Running)
    {
        //we are waiting for a signal to tell us when to start again
        pthread_cond_wait(&Cond, &Lock);
    }
    pthread_mutex_unlock(&Lock);
    RemTime.tv_nsec = 0;
    do
    {
        nanosleep(&MyTime, &RemTime);
    }
    while(RemTime.tv_nsec != 0);
    //select(0, NULL, NULL, NULL, &MyTime);
    if (myInterruptHandler != NULL)
    {
        myInterruptHandler(myParams);
    }
}

Thanks.

Advertisement

Answer

Whenever a non-masked signal arises, it will interrupt any system call running. There is an SA_RESTART option (see man -s7 signal for details), but that will only restart certain system calls (and not, for example, sleep). Whatever signal you use, if a system call is interrupted, then subject to the SA_RESTART proviso above, behaviour will be altered. Therefore you will need to go through your entire application ensuring that a return code of EINTR is handled properly. Of course, that’s hard if you are using sleep and expecting reliability.

I would suggest you are best moving away from using signals, especially if you are using threads anyway; the two do not play nicely. You say you’d like it to be portable: another reason to avoid signals like the plague.

What I would do is set up another thread to do your timers, and protect the timer values with an appropriate mutex. That thread can sleep until it needs to run next, either using nanosleep or pthread_cond_timedwait (if you want a condition to break out of it). In either event, you will need to wrap the call to avoid signals causing a false exit. Read the current value of the clock before the call, wrap the call in a do/while() loop, and test that the time really has exceeded the time you wanted in the test condition. Traditionally one uses gettimeofday for this, but that isn’t always monotonic, so you might want clock_gettime with CLOCK_MONOTONIC depending on how you want to react to the system time changing.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement