Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Posix Timer periodically skips ahead half of its period

Tags:

c

linux

posix

timer

I have a task linked to a Posix Timer (timer_create()) that executes every 20 ms/50 Hz. Works fine for the most part, except every 334.5 seconds (approx.) the timer executes 10 ms early for the one cycle. For the next 334.5-ish seconds the intervals are all 20 ms again.

I've included the relevant code for configuring the timer. The application runs on a Gumstix Verdex Pro XL6P with Gumstix's default version of Linux. I also have it scheduled with FIFO scheduling algorithm.

My gut says this is a integer overflow issue. Maybe there's something else using the same signal? I've consistently been able to reproduce the skip, across executions and sessions on the board.

The issue's not a show stopper, but I would really like to understand why this is happening.

Here's the code for configuring the timer:

//------------------------------------------------------------------------------
// Create a timer which fires at the specified time and calls a timer event
// handler.
//
// handler : function to be called when the timer expires
// us      : number of microseconds to add to timer
// ms      : number of milliseconds to add to timer
// sec     : number of seconds to add to timer
//------------------------------------------------------------------------------
void createTimer(void (*handler)(void), uint32 us, uint32 ms, uint32 sec)
{
    struct sigaction sigact;
    struct sigevent sigev;
    timer_t timerid;
    struct itimerspec itval;
    struct itimerspec oitval;
    timer_info_t* newTimer = NULL;

    // Initialize signalNum.
    if (timers == NULL)
        signalNum = SIGRTMAX;

    if (signalNum < SIGRTMIN)
        exitWithError("no avaiable signals, unable to create timers");

    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = SA_SIGINFO;
    sigact.sa_sigaction = signalHandler;

    // Set up sigaction to catch signal
    if (sigaction(signalNum, &sigact, NULL) == -1)
        exitWithError("sigaction() failed, unabled to creat timers");

    // Create the POSIX timer to generate signo
    sigev.sigev_notify = SIGEV_SIGNAL;
    sigev.sigev_signo = signalNum;
    sigev.sigev_value.sival_ptr = &timerid;        

    long ret = timer_create(CLOCK_REALTIME, &sigev, &timerid);

    if (ret == 0) 
    {
        // Prevent overflow in calculation of nsec below.
        if (ms >= 1000)
        {
            sec += (ms / 1000);
            ms = ms % 1000;
        }

        itval.it_value.tv_sec = sec;
        itval.it_value.tv_nsec = (long)(us * 1000L) + (long)(ms * 1000L * 1000L);

        // Configure it as a repeat timer.
        itval.it_interval.tv_sec = itval.it_value.tv_sec;
        itval.it_interval.tv_nsec = itval.it_value.tv_nsec;

        if (timer_settime(timerid, 0, &itval, &oitval) != 0)
            exitWithError("time_settime() error!");
    }
    else
        exitWithError("timer_create() error!");

    newTimer = (timer_info_t*)malloc(sizeof(timer_info_t));
    newTimer->timer = timerid;
    newTimer->handler = handler;
    newTimer->sigNum = signalNum;
    newTimer->next = NULL;

    // Check to see if this is the first time through.
    if (timers == NULL)
    {
        timers = newTimer;
        atexit(deleteTimers);
    }
    else
        lastTimer->next = newTimer;

    lastTimer = newTimer;

    signalNum--;
}

Thanks in advance.

like image 428
Trev Sheerin Avatar asked Nov 13 '22 17:11

Trev Sheerin


1 Answers

My guess is that you're using ntp, and that it's slewing the time at those intervals. You could try to use CLOCK_MONOTONIC instead, but according to http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html it might also be affected.

like image 190
Per Johansson Avatar answered Dec 21 '22 08:12

Per Johansson