Reputation:
I use timerfd
with zmq.
How can I use timerfd_create
and timerfd_set
to wait one second for the timer (https://man7.org/linux/man-pages/man2/timerfd_create.2.html)?
I have looked through the link but I still do not get how I can initilize a timer that waits one second per tick with create and set. This is exactly my task:
We start a timer with
timerfd_create()
, which is 1 / sec. ticking. When setting a timer with timer_set_(..)
a counter is simply incremented, which is decremented with every tick. When the counter reaches 0, the timer has expired.
In this project we have a function timer _ set _()
, where the timer is set with the function timerfd_create and timerfd_settimer()
. I hope you can help me.
This is my progress (part of my code):
struct itimerspec timerValue;
g_items[n].socket = nullptr;
g_items[n].events = ZMQ_POLLIN;
g_items[n].fd = timerfd_create(CLOCK_REALTIME, 0);
if(g_items[n].fd == -1 ){
printf("timerfd_create() failed: errno=%d\n", errno);
return -1;
}
timerValue.it_value.tv_sec = 1;
timerValue.it_value.tv_nsec = 0;
timerValue.it_interval.tv_sec = 1;
timerValue.it_interval.tv_nsec = 0;
timerfd_settime(g_items[n].fd, 0, &timerValue, NULL);
Upvotes: 5
Views: 13031
Reputation: 7490
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.
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 ofnew_value.it_value
to a nonzero value arms the timer.
Setting both fields ofnew_value.it_value
to zero disarms the timer.
Setting one or both fields ofnew_value.it_interval
to nonzero values specifies the period, in seconds and nanoseconds, for repeated timer expirations after the initial expiration. If both fields ofnew_value.it_interval
are zero, the timer expires just once, at the time specified bynew_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.
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
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.
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:
read()
. We ignored them, but they contained the number of the expired timeoutsUpvotes: 9
Reputation: 807
With timerfds, the idea is that a read on the fd will return the number of times the timer has expired.
From the timerfd_settime(2) man page:
Operating on a timer file descriptor The file descriptor returned by timerfd_create() supports the following operations:
read(2) If the timer has already expired one or more times since its settings were last modified using timerfd_settime(), or since the last successful read(2), then the buffer given to read(2) returns an unsigned 8-byte integer (uint64_t) containing the number of expirations that have occurred.
If no timer expirations have occurred at the time of the read(2), then the call either blocks until the next timer expiration, or fails with the error EAGAIN if the file descriptor has been made nonblocking (via the use of the fcntl(2) F_SETFL operation to set the O_NONBLOCK flag).
So, basically, you create an unsigned 8 byte integer (uint64_t on Linux), and pass that to your read call.
uint64_t buf;
int expired = read( g_items[n].fd, &buf, sizeof(uint64_t));
if( expired < 0 ) perror("read");
Something like that, if you want to block until you get an expiry.
Upvotes: 1