Search code examples
c++ctimersignalsembedded-linux

When setting up an interrupt on timer expiry with signal.h and time.h, is it required to put the timer address in sigevent sigev_value.sival_ptr?


I'm currently setting up an interrupt on timer expiry with the Linux system programming libraries time.h and signal.h. In the example provided in this page of the manual, the sigevent field sigev_value.sival_ptr is set to point to the address of the pointer from which a signal should be created when it expires:

/* Create the timer. */

sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIG;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCKID, &sev, &timerid) == -1)
    errExit("timer_create");

printf("timer ID is %#jx\n", (uintmax_t) timerid);

If I'm not mistaken, putting that value there has no effect, as the pointer to the timer is already provided as an argument of the timer_create function. Why is it written in this way? Is it because of the way the signal handler is registered ?

/* Establish handler for timer signal. */

           printf("Establishing handler for signal %d\n", SIG);
           sa.sa_flags = SA_SIGINFO;
           sa.sa_sigaction = handler;
           sigemptyset(&sa.sa_mask);
           if (sigaction(SIG, &sa, NULL) == -1)
               errExit("sigaction");

In this case, I also don't understand why sa_sigaction is use to point to the signal handling function. Since we don't initialize the siginfo_t struct to convey more information about the signal, shouldn't we use the sa_handler member instead?

I tried to run the example by not setting the sigev_value.sival_ptr and using sa_handler instead, and it worked without any issue, even though I thought it wouldn't. Here's a copy of the code I ran:

#include "RLED.hpp"
#include <iostream>
#include <signal.h>
#include <time.h>
#include <unistd.h>

bool value = false;
R::LED blueLED( 0, 22 );

void timer_handler(int signum) {
    value = !(value);
    if (value == 1)
    {
        blueLED.set();
    }
    else
    {
        blueLED.clear();
    }
}

int main()
{
    struct sigaction sa;
    struct sigevent sev;
    timer_t timerid;
    struct itimerspec its;

    // Set up the signal handler for the timer signal
    sa.sa_handler = timer_handler;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGRTMIN, &sa, NULL) == -1)
    {
        std::cout << "Error: signal cannot be setup" << std::endl;
    }

    // Create the timer
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGRTMIN;
    sev.sigev_value.sival_ptr = &timerid;
    timer_create(CLOCK_REALTIME, &sev, &timerid);

    // Configure the timer to expire every 1 second
    its.it_value.tv_sec = 1;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 1;
    its.it_interval.tv_nsec = 0;
    timer_settime(timerid, 0, &its, NULL);

    for (uint32_t i = 0; i < 10; i++)
    {
        sleep(1);
        std::cout << i << std::endl;
    }

    struct itimerspec curr_value;
    timer_gettime(timerid, &curr_value);
    std::cout << "Time: " << curr_value.it_value.tv_sec << "." << curr_value.it_value.tv_nsec << std::endl;

    its = {{0, 0}, {0, 0}};
    timer_settime(timerid, 0, &its, NULL);

    timer_delete(timerid);
    sigaction(SIGRTMIN, NULL, NULL);
    
    return 0;
}

Solution

  • the sigevent field sigev_value.sival_ptr is set to point to the address of the pointer from which a signal should be created when it expires:

    That's an awkward way to phrase it. How about "the member is set to point to the location of the timer ID."

    If I'm not mistaken, putting that value there has no effect, as the pointer to the timer is already provided as an argument of the timer_create function.

    If you mean that timer_create() does not use that value directly or rely on it being set, then yes, you are correct.

    Or if you say that in the context that the example code also sets

    sev.sigev_notify = SIGEV_SIGNAL;
    

    then I would also say that for notification by signal, nothing takes any notice of the value set in the sigev_value.

    Why is it written in this way? Is it because of the way the signal handler is registered ?

    No particular technical reason. The field is relevant for notification by callback, however, so if there is an accompanying example of that, then perhaps the object was to minimize the differences between the two examples.

    I also don't understand why sa_sigaction is use to point to the signal handling function. Since we don't initialize the siginfo_t struct to convey more information about the signal, shouldn't we use the sa_handler member instead?

    I have no idea what you mean by "we don't initialize the siginfo_t struct". Such initialization is not the responsibility of the application. You choose between two options for the signature of the signal handler function. You set the appropriate flag and use either sa_handler or sa_sigaction, depending on which signature you chose. If the latter, the system provides an appropriate siginfo_t * for each signal.

    I tried to run the example by not setting the sigev_value.sival_ptr and using sa_handler instead, and it worked without any issue, even though I thought it wouldn't.

    You have specified a signal handler function with the appropriate signature for sa_handler. sigev_value is not relevant to notification by signal. I have not reviewed your full example code, but those details look fine to me.