budhe888
budhe888

Reputation: 31

Repeating timerfd event works with epoll and not with poll

I am implementing a timer using timerfd. This is a relative timer that I just need to repeat forever at the rate it is set to. I want to poll on this event and originally tried using poll. When I did this, I would see the timer event the first time and then never again. However, when I changed to using epoll (no change at all to how the timerfd was set up) it works as expected.

Here is the code with poll:

#include <sys/timerfd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

int main(int ac, char *av[])
{
    struct pollfd p;
    int timerfd;
    struct itimerspec timerValue;

    /* clear pollfd */
    bzero(&p, sizeof(p));

    /* set timerfd */
    timerfd = timerfd_create(CLOCK_REALTIME, 0);
    if (timerfd < 0) {
        printf("failed to create timer fd\n");
        exit(1);
    }
    bzero(&timerValue, sizeof(timerValue));
    timerValue.it_value.tv_sec = 1;
    timerValue.it_value.tv_nsec = 0;
    timerValue.it_interval.tv_sec = 1;
    timerValue.it_interval.tv_nsec = 0;

    /* set events */
    p.fd = timerfd;
    p.revents = 0;
    p.events = POLLIN;

    /* start timer */
    if (timerfd_settime(timerfd, 0, &timerValue, NULL) < 0) {
        printf("could not start timer\n");
        exit(1);
    }

    /* wait for events */
    while (1) {
        int numEvents = poll(&p, 1, -1);
        if (numEvents > 0) {
            int timersElapsed = 0;
            (void) read(p.fd, &timersElapsed, 8);
            printf("timers elapsed: %d\n", timersElapsed);
        }
    }

    exit(0);
}

And here is the code with epoll:

#include <sys/timerfd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

int main(int ac, char *av[])
{
    struct epoll_event epollEvent;
    struct epoll_event newEvents;
    int timerfd;
    int epollfd;
    struct itimerspec timerValue;

    /* set timerfd */
    timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timerfd < 0) {
        printf("failed to create timer fd\n");
        exit(1);
    }
    bzero(&timerValue, sizeof(timerValue));
    timerValue.it_value.tv_sec = 1;
    timerValue.it_value.tv_nsec = 0;
    timerValue.it_interval.tv_sec = 1;
    timerValue.it_interval.tv_nsec = 0;

    /* set events */
    epollfd = epoll_create1(0);
    epollEvent.events = EPOLLIN;
    epollEvent.data.fd = timerfd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd, &epollEvent);

    /* start timer */
    if (timerfd_settime(timerfd, 0, &timerValue, NULL) < 0) {
        printf("could not start timer\n");
        exit(1);
    }

    /* wait for events */
    while (1) {
        int numEvents = epoll_wait(epollfd, &newEvents, 1, 0);
        if (numEvents > 0) {
            int timersElapsed = 0;
            (void) read(epollEvent.data.fd, &timersElapsed, 8);
            printf("timers elapsed: %d\n", timersElapsed);
        }
    }

    exit(0);
}

Any idea what I might be doing wrong with poll? Maybe it is not meant to be used this way with a timerfd? Thank you.

Upvotes: 3

Views: 5553

Answers (3)

John
John

Reputation: 51

I faced the same problem.

After debugging, the root cause in poll example is that timerValue should be declared as uint64_t.

 - int timersElapsed = 0;
 + uint64_t timersElapsed = 0;

The man page of timerfd_create() describes this.

   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  suc‐
              cessful 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.   (The  returned value is in host byte order—that is,
              the native byte order for integers on the host machine.)

Upvotes: 0

Peter Andersson
Peter Andersson

Reputation: 51

Ok, this is an old question, but nevertheless. The problem lies in these lines of code:

   int timersElapsed = 0;
   (void) read(p.fd, &timersElapsed, 8);
   printf("timers elapsed: %d\n", timersElapsed);

int timersElapsed is 4 bytes. Reading 8 bytes into this results in a stack overflow, giving unpredictable behaviour.

Changing timersElapsed to a long int and fixing the printf did the trick for me.

   long int timersElapsed = 0;
   (void) read(p.fd, &timersElapsed, 8);
   printf("timers elapsed: %ld\n", timersElapsed);

Upvotes: 5

budhe888
budhe888

Reputation: 31

This appears to be an issue with Fedora (or my installation of Fedora). That system is running 3.16, and poll() does not work.

However, on a separate Ubuntu installation with 3.13, the poll() code above works just fine. As I will be using Ubuntu in the future anyway, I will not try to track down the issue on Fedora. Though I am curious if others are seeing this same issue on Fedora systems.

Upvotes: 0

Related Questions