I found the below code in the Internet, I'm trying to understand how Linux timer works, anyway, as you can see below the counter1 is global var, what will happen if the while is working on it and the timer goes off and changed the value of counter1, do I need a lock in there?
// timertst1.c: Simple timer demo program. Use cross-gcc
// Vers. 1.00 - 21.Oct. 2002
// k.d.walter@t-online.de
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
// This is a timer handler.
int counter1 = 0;
void timerHandler (int signum)
{
printf ("timerHandler: counter= %d\n", counter1++);
fflush (stdout);
}
// This is the one and only main function.
int main (void)
{
struct sigaction sa;
struct itimerval timer;
// Install the timer handler...
memset (&sa, 0, sizeof (sa));
sa.sa_handler= &timerHandler;
sigaction (SIGALRM, &sa, NULL);
// Configure the timer to expire every 100 msec...
timer.it_value.tv_sec= 0; // First timeout
timer.it_value.tv_usec= 500000;
timer.it_interval.tv_sec= 0; // Interval
timer.it_interval.tv_usec= 500000;
// Start timer...
setitimer (ITIMER_REAL, &timer, NULL); setitimer (ITIMER_REAL, &timer, NULL);
// Do noting...
while (1) {
printf("i'm here waiting to be interuppted = %d\n",counter1);
//some work;
counter1++;
//some other work;
}
}
Signal handlers are dangerous. The OS is going to interrupt your program, whatever it was doing, and run the handler; when the handler returns, the OS will take your program back to what it was doing.
Let's say that
counter1++
is compiled to a load, an increment and a store. If the interrupt fires between the load and the store, then the sequence of instructions will be load, load, increment, increment, store, store. The variable will go up by one, not two; disaster! And you're right, this looks like a classic multi-threading issue.What happens if we add a lock? Now instead of load, increment, store, we get lock, load, increment, store, unlock. But if the signal fires while we're between lock and unlock, the OS jumps us right into the handler - it doesn't let our code unlock first. When the handler tries to lock, main() is still holding the lock, so we deadlock. Disaster!
The safe thing to do is to write your signal handler so it just makes a note of what has to be done, and write code to take care of actually doing that elsewhere. But even that is not necessarily simple - what if your signal handler is itself interrupted? Not a problem in this particular case, but worth considering an example of how deep the rabbit hole goes. Writing correct signal handlers is hard. There are some tricks, for instance you can block signal handlers. But it's still hard.
You can also consider using
sig_atomic_t
for safely communicating from a signal handler with the rest of the program. This, however, would not work in the example that you posted since reading, incrementing and storing a value is not an atomic operation.Here you have an excerpt from GLIBC manual explaining
sig_atomic_t
:And here a usage example: