Having looked at all this wonderful theory, let's turn our attention to some specific code samples to see what you can do with timers. To work with a timer, you must:
Let's look at these in order.
The first step is to create the timer with timer_create():
#include <time.h>
#include <sys/siginfo.h>
int
timer_create (clockid_t clock_id,
struct sigevent *event,
timer_t *timerid);
The clock_id argument tells the timer_create() function which time base you're creating this timer for. This is a POSIX thing — POSIX says that on different platforms you can have multiple time bases, but that every platform must support at least the CLOCK_REALTIME time base. Under BlackBerry 10 OS, there are three time bases to choose from:
For now, we ignore CLOCK_SOFTTIME and CLOCK_MONOTONIC but we come back to them in Other clock sources.
The second parameter is a pointer to a struct sigevent data structure. This data structure is used to inform the kernel about what kind of event the timer should deliver whenever it fires. We discussed how to fill in the struct sigevent in the discussion of signals versus pulses versus thread creation.
So, you can call timer_create() with CLOCK_REALTIME and a pointer to your struct sigevent data structure, and the kernel creates a timer object for you (which gets returned in the last argument). This timer object is just a small integer that acts as an index into the kernel's timer tables; think of it as a handle.
At this point, nothing else is going to happen. You've only just created the timer; you haven't triggered it yet.
Having created the timer, you now have to decide what kind of timer it is. This is done by a combination of arguments to timer_settime(), the function used to actually start the timer:
#include <time.h>
int
timer_settime (timer_t timerid,
int flags,
struct itimerspec *value,
struct itimerspec *oldvalue);
The timerid argument is the value that you got back from the timer_create() function call — you can create a bunch of timers, and then call timer_settime() on them individually to set and start them at your convenience.
The flags argument is where you specify absolute versus relative.
If you pass the constant TIMER_ABSTIME, then it's absolute, pretty much as you expect. You then pass the actual date and time when you want the timer to go off.
If you pass a zero, then the timer is considered relative to the current time.
Let's look at how you specify the times. Here are key portions of two data structures (in <time.h>):
struct timespec {
long tv_sec,
tv_nsec;
};
struct itimerspec {
struct timespec it_value,
it_interval;
};
There are two members in struct itimerspec:
The it_value specifies either how long from now the timer should go off (in the case of a relative timer), or when the timer should go off (in the case of an absolute timer). Once the timer fires, the it_interval value specifies a relative value to reload the timer with so that it can trigger again. Note that specifying a value of zero for the it_interval makes it into a one-shot timer. You might expect that to create a pure periodic timer, you just set the it_interval to the reload value, and set it_value to zero. Unfortunately, the last part of that statement is false — setting the it_value to zero disables the timer. If you want to create a pure periodic timer, set it_value equal to it_interval and create the timer as a relative timer. This fires once (for the it_value delay) and then keeps reloading with the it_interval delay.
Both the it_value and it_interval members are actually structures of type struct timespec, another POSIX thing. The structure lets you specify sub-second resolutions. The first member, tv_sec, is the number of seconds; the second member, tv_nsec, is the number of nanoseconds in the current second. (What this means is that you should never set tv_nsec past the value 1 billion — this would imply more than a one-second offset.)
Here are some examples:
it_value.tv_sec = 5; it_value.tv_nsec = 500000000; it_interval.tv_sec = 0; it_interval.tv_nsec = 0;
This creates a one-shot timer that goes off in 5.5 seconds. (We got the .5 because of the 500,000,000 nanoseconds value.)
We're assuming that this is used as a relative timer, because if it weren't, then that time would have elapsed long ago (5.5 seconds past January 1, 1970, 00:00 GMT).
Here's another example:
it_value.tv_sec = 987654321; it_value.tv_nsec = 0; it_interval.tv_sec = 0; it_interval.tv_nsec = 0;
This creates a one-shot timer that goes off Thursday, April 19, 2001 at 00:25:21 EDT. (There are a bunch of functions that help you convert between the human-readable date and the number of seconds since January 1, 1970, 00:00:00 GMT representation. Take a look in the C library at time() , asctime() , ctime() , mktime() , strftime() , and so on).
For this example, we're assuming that it's an absolute timer, because of the huge number of seconds that we'd be waiting if it were relative (987654321 seconds is about 31.3 years).
Note that in both examples, there's nothing in the code for timer_settime() that checks those assumptions and does the right thing! You have to specify whether the timer is absolute or relative yourself. The kernel happily schedules something 31.3 years into the future.
One last example:
it_value.tv_sec = 1; it_value.tv_nsec = 0; it_interval.tv_sec = 0; it_interval.tv_nsec = 500000000;
Assuming it's relative, this timer goes off in one second, and then again every half second after that. There's absolutely no requirement that the reload values look anything like the one-shot values.
So far, we've seen just about all there is to see with timers, except for one small thing. We've been delivering messages (via a pulse), but you can also deliver POSIX signals. Let's see how this is done:
timer_create (CLOCK_REALTIME, NULL, &timerid);
This is the simplest way to create a timer that sends you a signal. This method raises SIGALRM when the timer fires. If we had actually supplied a struct sigevent, we can specify which signal we actually want to get:
struct sigevent event; SIGEV_SIGNAL_INIT (&event, SIGUSR1); timer_create (CLOCK_REALTIME, &event, &timerid);
This hits us with SIGUSR1 instead of SIGALRM.
You catch timer signals with normal signal handlers; there's nothing special about them.
If you want to create a new thread every time a timer fires, then you can do so with the struct sigevent and all the other timer stuff we have discussed:
struct sigevent event; SIGEV_THREAD_INIT (&event, maintenance_func, NULL);
You need to be particularly careful with this one, because if you specify too short an interval, you'll be flooded with new threads! This can eat up all your CPU and memory resources!