The question appears about setting correctly the timeouts of the timer.
With the settings
timerValue.it_value.tv_sec = 1;
timerValue.it_value.tv_nsec = 0;
timerValue.it_interval.tv_sec = 1;
timerValue.it_interval.tv_nsec = 0;
You are correctly setting the initial timeout to 1s (field timerValue.it_value
). But you are also setting a periodic interval of 1s, and you didn't mention the will to do it.
About the timeouts
This behavior is described by the following passage of the manual:
int timerfd_create(int clockid, int flags);
new_value.it_value
specifies the initial expiration of the timer, in seconds and nanoseconds. Setting either field of new_value.it_value
to a nonzero value arms the timer.
Setting both fields of new_value.it_value
to zero disarms the timer.
Setting one or both fields of new_value.it_interval
to nonzero values specifies the period, in seconds and nanoseconds, for repeated timer expirations after the initial expiration. If both fields of new_value.it_interval
are zero, the timer expires just once, at the time specified by new_value.it_value
.
The emphasis on the last paragraph is mine, as it shows what to do in order to have a single-shot timer.
The benefits of timerrfd
. How to detect timer expiration?
The main advantage provided by timerfd
is that the timer is associated to a file descriptor, and this means that it
may be monitored by select
(2), poll
(2), and epoll
(7).
The information contained in the other answer about read()
is valid as well: let's just say that, even using functions such as select()
, read()
function will be required in order to consume data in the file descriptor.
A complete example
In the following demonstrative program, a timeout of 4 seconds is set; after that a periodic interval of 5 seconds is set.
The good old select()
is used in order to wait for timer expiration, and read()
is used to consume data (that is the number of expired timeouts; we will ignore it).
#include <stdio.h>
#include <sys/timerfd.h>
#include <sys/select.h>
#include <time.h>
int main()
{
int tfd = timerfd_create(CLOCK_REALTIME, 0);
printf("Starting at (%d)...\n", (int)time(NULL));
if(tfd > 0)
{
char dummybuf[8];
struct itimerspec spec =
{
{ 5, 0 }, // Set to {0, 0} if you need a one-shot timer
{ 4, 0 }
};
timerfd_settime(tfd, 0, &spec, NULL);
/* Wait */
fd_set rfds;
int retval;
/* Watch timefd file descriptor */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(tfd, &rfds);
/* Let's wait for initial timer expiration */
retval = select(tfd+1, &rfds, NULL, NULL, NULL); /* Last parameter = NULL --> wait forever */
printf("Expired at %d! (%d) (%d)\n", (int)time(NULL), retval, read(tfd, dummybuf, 8) );
/* Let's wait (twice) for periodic timer expiration */
retval = select(tfd+1, &rfds, NULL, NULL, NULL);
printf("Expired at %d! (%d) (%d)\n", (int)time(NULL), retval, read(tfd, dummybuf, 8) );
retval = select(tfd+1, &rfds, NULL, NULL, NULL);
printf("Expired at %d! (%d) (%d)\n", (int)time(NULL), retval, read(tfd, dummybuf, 8) );
}
return 0;
}
And here it is the output. Every row contains also the timestamp, so that the actual elapsed time can be checked:
Starting at (1596547762)...
Expired at 1596547766! (1) (8)
Expired at 1596547771! (1) (8)
Expired at 1596547776! (1) (8)
Please note:
- We just performed 3 reads, for test
- The intervals are 4s + 5s + 5s (initial timeout + two interval timeouts)
- 8 bytes are returned by
read()
. We ignored them, but they contained the number of the expired timeouts