Working with timer_create() to use multiple timers using a single signal 5


The man page for timer_create() (used in applications, not in kernel code) gives a useful example of how to create and start high resolution timers.  However, it fails to mention that if you use multiple timers with a single signal then you need have a single signal handler that can identify which timer just went off and pass control to the timer-specific handler.

The way to do this is as follows.  Start with a function that creates and starts timers, makeTimer(). 

static int
makeTimer( char *name, timer_t *timerID, int expireMS, int intervalMS )
{
    struct sigevent         te;
    struct itimerspec       its;
    struct sigaction        sa;
    int                     sigNo = SIGRTMIN;

    /* Set up signal handler. */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timerHandler;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sigNo, &sa, NULL) == -1)
    {
        fprintf(stderr, "%s: Failed to setup signal handling for %s.\n", PROG, name);
        return(-1);
    }

    /* Set and enable alarm */
    te.sigev_notify = SIGEV_SIGNAL;
    te.sigev_signo = sigNo;
    te.sigev_value.sival_ptr = timerID;
    timer_create(CLOCK_REALTIME, &te, timerID);

    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = intervalMS * 1000000;
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = expireMS * 1000000;
    timer_settime(*timerID, 0, &its, NULL);

    return(0);
}

The function takes a pointer to a timer_t variable that will be filled with the timer ID created by timer_create().  This pointer is also saved in the sival_ptr variable right before calling timer_create().  In this function notice that we always use the SIGRTMIN signal, so expiration of any timer causes this signal to be raised.  The signal handler I've written for that signal is timerHandler

static void
timerHandler( int sig, siginfo_t *si, void *uc )
{
    timer_t *tidp;
    tidp = si->si_value.sival_ptr;

    if ( *tidp == firstTimerID )
        firstCB(sig, si, uc);
    else if ( *tidp == secondTimerID )
        secondCB(sig, si, uc);
    else if ( *tidp == thirdTimerID )
        thirdCB(sig, si, uc);
}

The handler checks that the value stored in sival_ptr matches a given timerID variable.  The sival_ptr is the same as the one we set in makeTimer(), though here it lives in a different structure.  Obviously, it got copied from there to here on the way to this signal handler.  The point is that the timerID is what is used to determine which timer just went off and determine what to do next.

All that's left is to create a timer.  Here's an example.

timer_t firstTimerID;

static int
srtSchedule( void )
{
    int rc;
    rc = makeTimer("First Timer", &firstTimerID, 40, 40);
    return rc;
}

This schedules a timer to go off every 40 milliseconds.  On my board (an embedded PowerPC-based board) the high resolution timers have a resolution of 4ms (see clock_getres()) so 40ms shouldn't be a problem.  The callback associated with this timer, firstCB(), is not shown here because it's specific to the application.

There is a lot of other stuff you can do with these timers.  You can check if the time overrun should cause an adjustment in the timer.  That helps sync with the real time clock.  Also, as with interrupts in the kernel, you should keep signal handlers fast and efficient.  Don't do anything that takes a very long time to complete.  Typically giving a thread a kick to do something should be sufficient.  But that really depends on your application.

 


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 thoughts on “Working with timer_create() to use multiple timers using a single signal

  • Praveen Kumar Bairi

    Is this really works for you.
    i am creating multiple timers, say 3 timers from a single process (Ex: main ()). and all are supposed to land in single Timer handler.
    But its giving bus error and "si->si_value.sival_ptr" ==> is of junk value and some how it is getting corrupted.

    Please help me out.

  • mjhammel Post author

    Yes, I know this code works. It’s in use in a radar for some non-critical timing sections.

    If sival_ptr has junk then you probably fed it junk, which means you probably have some kind of buffer overflow in your code that is causing the call to timer_create() to be incorrect. You should do what I did when I was working on this code: strip out makeTimer() and timerHandler() into a small test program that has nothing else in it except your main(). Have the main() call makeTimer() and have timerHandler() print out what it’s trying to do. Use this test program to prove these timer-related wrapper functions are working on their own.

    This is really nothing more than a wrapper afound the timer system calls so it should just work. All this code does is, essentially, overload a signal to allow multiple paths to be followed. Keep in mind that your signal handler (such as firstCB() in my example code) should be fast and not allocate or free memory. You don’t want one handler blocking another. So the handlers should just update some static variable and let a thread monitor that variable for changes.

    One problem I ran into with this code was trying to deal with the signals in a thread that was not the main thread of the application. Thread-specific signal handling didn’t work right (I may have been using it incorrectly) so I moved all signal handling into the main thread to get around that issue. The main signal handlers use thread locks to update thread variables in order to notify a thread loop to perform some action.

    Other than that, I don’t know what to tell you. Good luck.

  • Eike

    Hi,
    at first i want to thank you for your explainations; the man-page example for timer_create is a bit bloated and confusing and the configuration-possibilities of the timers are totally overwhelming. You stripped it down to the essential, making it possible, to understand the basics quite well.

    I just want to to add/correct a thing that costed me much time to find out:
    When working with bigger times than about 2,4s=2400ms, your code starts behaving not as exspected. This is caused by the lines:

    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = intervalMS * 1000000;
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = expireMS * 1000000; 


    The variables its.it_value.tv_nsec and its.it_intervall.tv_nsec are of type long and have a range up to 2 147 483 647 which is more than 2400ms=2400ms*1000000ns/ms=2400000000ns . This causes an overflow and the Timer does not fire at the expected times. A solution is found in the mentioned bloated man-page of timer_create:
    its.it_value.tv_sec = freq_nanosecs / 1000000000;
    its.it_value.tv_nsec = freq_nanosecs % 1000000000;

    It seems as if the Timer-Periode is composed from the sum of the fields in it_value. In the man-page example the author therefore at first fills tv_sec with the full-seconds-component of the interval the timer should fire at. Then he takes the remaining nanoseconds by using the modulo-operator and puts it in tv_nsec.
    Because the script on this page works with milliseconds, you would do something like the following:

    its.it_value.tv_sec = expireMs / 1000;

    its.it_value.tv_nsec = (expireMs % 1000)*1000000;

    And do the same for it_interval.
    This should give you a Timer that also works for bigger intervals.

    Have a nice day,
    Eike