Search code examples
ctimerposixsigaction

Do I have to use a signal handler for a Posix timer?


I want to start a timer and have a function called when it expires.

Googling finds lots of examples, including the example in the manual, all of which use sigaction() to set a signal handler.

However, @Patryk says in this question that we can just

void cbf(union sigval);
struct sigevent sev;
timer_t timer;

sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = cbf; //this function will be called when timer expires
sev.sigev_value.sival_ptr = (void*) arg;//this argument will be passed to cbf
timer_create(CLOCK_MONOTONIC, &sev, &timer);

which is shorter, simpler, cleaner, more maintainable ...

What gives? Is this correct? Is it just a wrapper for sigaction()? Why do the examples explicitly set a signal handler?

Also, if I start a timer either by this method, or by timer_settime and a signal handler, will cancelling the timer casue the system to remove the association between that timer and the callback, or do I have to do that explicitly?


[Update] You can choose either signals or the method I show in my answer below (or both, but that seems silly). It is a matter of taste. Singals might offer a little more functionality, at the cost of complification.

If all you want to do is start a timer and be notified when it expires, the method in my answer is simplest.


Solution

  • It seems that I do not have to use a signal handler and can make the code much simpler, as shown here:

    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <time.h>
    #include <unistd.h>
    
    static unsigned int pass_value_by_pointer = 42;
    
    void Timer_has_expired(union sigval timer_data)
    {
        printf("Timer expiration handler function; %d\n", *(int *) timer_data.sival_ptr);
    }
    
    int main(void)
    {
        struct sigevent timer_signal_event;
        timer_t timer;
    
        struct itimerspec timer_period;
    
        printf("Create timer\n");
        timer_signal_event.sigev_notify = SIGEV_THREAD;
        timer_signal_event.sigev_notify_function = Timer_has_expired;       // This function will be called when timer expires
        // Note that the following is a union. Assign one or the other (preferably by pointer)
        //timer_signal_event.sigev_value.sival_int = 38;                        // This argument will be passed to the function
        timer_signal_event.sigev_value.sival_ptr = (void *) &pass_value_by_pointer;     // as will this (both in a structure)
        timer_signal_event.sigev_notify_attributes = NULL;
        timer_create(CLOCK_MONOTONIC, &timer_signal_event, &timer);
    
        printf("Start timer\n");
        timer_period.it_value.tv_sec = 1;                                   // 1 second timer
        timer_period.it_value.tv_nsec = 0;                                  // no nano-seconds
        timer_period.it_interval.tv_sec = 0;                                // non-repeating timer
        timer_period.it_interval.tv_nsec = 0;
    
        timer_settime(timer, 0, &timer_period, NULL);
        sleep(2);
    
        printf("----------------------------\n");
        printf("Start timer a second time\n");
        timer_settime(timer, 0, &timer_period, NULL);
        sleep(2);
    
        printf("----------------------------\n");
        printf("Start timer a third time\n");
        timer_settime(timer, 0, &timer_period, NULL);
    
        printf("Cancel timer\n");
        timer_delete(timer);
        sleep(2);
        printf("The timer expiration handler function should not have been called\n");
    
        return EXIT_SUCCESS;
    }
    

    when run, it gives this output:

    Create timer
    Start timer
    Timer expiration handler function; 42
    ----------------------------
    Start timer a second time
    Timer expiration handler function; 42
    ----------------------------
    Start timer a third time
    Cancel timer
    The timer expiration handler function should not have been called