clinuxsignalsreentrancy

Why can sig_atomic_t be used in a reentrant function


I've been developing C++ on Linux for a long time. And when I develop some independent module processing a message/task queue, I always process the SIGINT signal to avoid message/task loss. Here is an example of my code:

volatile sig_atomic_t sig = 0;

void sig_handler(int signal)
{
    sig = 1;
}

int main()
{
    signal(SIGINT,sig_handler);

    msg_queue = init_msg_queue();
    init_receiving_msg_thread();    // start a thread to receive msgs and push them into msg_queue
    while(!sig) {
        process_msg(msg_queue.top());    // process the first msg in the queue
        msg_queue.pop();    // remove the first msg
    }
    stop_receiving_msg_thread();
    process_all_msgs(msg_queue);
    return 0;
}

Well, this piece of code is simple: if the signal SIGINT is captured, stop receiving messages, process all of messages left in the queue and return. Otherwise, the code will stay in the infinite while.

I thought the sig_atomic_t was some black magic. Because as my understanding, the function sig_handler must be a reentrant function, meaning that it can't hold any static or global non-constant data: What exactly is a reentrant function?

So I always thought that sig_atomic_t was some tricky stuff, instead of a global variable.

But today I read this link: How does sig_atomic_t actually work?, which told me that sig_atomic_t is nothing but simply a typedef, such as an int. So it seems that sig_atomic_t sig is simply a global variable.

Now I'm confused.

Did my code above use sig_atomic_t correctly? If not, could you please show me a correct example? If my code is correct, what did I misunderstand? sig_atomic_t is not a global varible? Or a global variable can be used in a reentrant function? Or the function sig_handler can be non-reentrant function?


Solution

  • Your use of volatile sig_atomic_t is correct.

    The manpage for signal(3p) says the following:

       "the behavior is undefined if the signal handler refers to any
       object other than errno with static storage duration other than
       by assigning a value to an object declared as volatile
       sig_atomic_t," ...
    

    So why does volatile sig_atomic_t have this ability while other variables are unsafe?

    The link you provided actually answers that. "it is guaranteed to be read and written in one go." But to elaborate, sig_atomic_t is actually not an atomic type, however it essentially acts like one for signal interrupt purposes. But it is generally not suited for multithreading purposes so do not use it for inter-thread communication.

    A true atomic type is actually specially handled by the CPU where it will make sure that only one core (cpu) can perform a whole operation (some manipulations take multiple instructions to complete) before another CPU (or process) can manipulate that data. sig_atomic_t on the other hand does not provide that hardware guarantee, but instead insures that any operation performed on it (read/write) will be done with a single instruction, thus the ability for a reentrant of that function to interrupt the completion of that value manipulation is eliminated.