eloop: add timer support

Add support for dispatching events every given interval. timerfd is used
for hooking up to the event loop, similar to how signalfd is used.

Only relative, monotonic and possibly repeating timer events are
supported. It can be enhanced if there's ever a need.

Signed-off-by: Ran Benita <ran234@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
Ran Benita 2012-02-01 22:35:02 +01:00 committed by David Herrmann
parent 8fa3336a6b
commit fb14273bf7
2 changed files with 184 additions and 0 deletions

View File

@ -35,9 +35,11 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include "eloop.h"
@ -81,6 +83,14 @@ struct kmscon_signal {
void *data;
};
struct kmscon_timer {
unsigned long ref;
struct kmscon_fd *fd;
kmscon_timer_cb cb;
void *data;
};
int kmscon_idle_new(struct kmscon_idle **out)
{
struct kmscon_idle *idle;
@ -479,6 +489,160 @@ void kmscon_eloop_rm_signal(struct kmscon_signal *sig)
*/
}
int kmscon_timer_new(struct kmscon_timer **out)
{
struct kmscon_timer *timer;
int ret;
if (!out)
return -EINVAL;
timer = malloc(sizeof(*timer));
if (!timer)
return -ENOMEM;
memset(timer, 0, sizeof(*timer));
timer->ref = 1;
ret = kmscon_fd_new(&timer->fd);
if (ret) {
free(timer);
return ret;
}
*out = timer;
return 0;
}
void kmscon_timer_ref(struct kmscon_timer *timer)
{
if (!timer)
return;
++timer->ref;
}
void kmscon_timer_unref(struct kmscon_timer *timer)
{
if (!timer || !timer->ref)
return;
if (--timer->ref)
return;
kmscon_fd_unref(timer->fd);
free(timer);
}
int kmscon_eloop_new_timer(struct kmscon_eloop *loop, struct kmscon_timer **out,
const struct itimerspec *spec, kmscon_timer_cb cb, void *data)
{
struct kmscon_timer *timer;
int ret;
if (!out)
return -EINVAL;
ret = kmscon_timer_new(&timer);
if (ret)
return ret;
ret = kmscon_eloop_add_timer(loop, timer, spec, cb, data);
if (ret) {
kmscon_timer_unref(timer);
return ret;
}
kmscon_timer_unref(timer);
*out = timer;
return 0;
}
static void timer_cb(struct kmscon_fd *fd, int mask, void *data)
{
struct kmscon_timer *timer = data;
uint64_t expirations;
int len;
if (mask & KMSCON_READABLE) {
len = read(fd->fd, &expirations, sizeof(expirations));
if (len != sizeof(expirations))
log_warn("eloop: cannot read timerfd\n");
else
timer->cb(timer, expirations, timer->data);
}
}
int kmscon_eloop_add_timer(struct kmscon_eloop *loop,
struct kmscon_timer *timer, const struct itimerspec *spec,
kmscon_timer_cb cb, void *data)
{
int ret, fd;
if (!loop || !timer || !spec || !cb)
return -EINVAL;
if (timer->fd->loop)
return -EALREADY;
fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
if (fd < 0)
return -errno;
ret = timerfd_settime(fd, 0, spec, NULL);
if (ret) {
ret = -errno;
log_warn("eloop: cannot set timerfd: %m\n");
goto err_fd;
}
ret = kmscon_eloop_add_fd(loop, timer->fd, fd, KMSCON_READABLE,
timer_cb, timer);
if (ret)
goto err_fd;
timer->cb = cb;
timer->data = data;
kmscon_timer_ref(timer);
return 0;
err_fd:
close(fd);
return ret;
}
void kmscon_eloop_rm_timer(struct kmscon_timer *timer)
{
int fd;
if (!timer || !timer->fd->loop)
return;
fd = timer->fd->fd;
kmscon_eloop_rm_fd(timer->fd);
close(fd);
kmscon_timer_unref(timer);
}
int kmscon_eloop_update_timer(struct kmscon_timer *timer,
const struct itimerspec *spec)
{
int ret;
if (!timer || !timer->fd->loop)
return -EINVAL;
ret = timerfd_settime(timer->fd->fd, 0, spec, NULL);
if (ret) {
ret = -errno;
log_warn("eloop: cannot set timerfd: %m\n");
return ret;
}
return 0;
}
int kmscon_eloop_new(struct kmscon_eloop **out)
{
struct kmscon_eloop *loop;

View File

@ -34,17 +34,22 @@
#ifndef KMSCON_ELOOP_H
#define KMSCON_ELOOP_H
#include <inttypes.h>
#include <stdlib.h>
#include <time.h>
struct kmscon_eloop;
struct kmscon_idle;
struct kmscon_fd;
struct kmscon_signal;
struct kmscon_timer;
typedef void (*kmscon_idle_cb) (struct kmscon_idle *idle, void *data);
typedef void (*kmscon_fd_cb) (struct kmscon_fd *fd, int mask, void *data);
typedef void (*kmscon_signal_cb)
(struct kmscon_signal *sig, int signum, void *data);
typedef void (*kmscon_timer_cb)
(struct kmscon_timer *timer, uint64_t num, void *data);
enum kmscon_eloop_flags {
KMSCON_READABLE = 0x01,
@ -98,4 +103,19 @@ int kmscon_eloop_add_signal(struct kmscon_eloop *loop,
struct kmscon_signal *sig, int signum, kmscon_signal_cb cb, void *data);
void kmscon_eloop_rm_signal(struct kmscon_signal *sig);
/* timer sources */
int kmscon_timer_new(struct kmscon_timer **out);
void kmscon_timer_ref(struct kmscon_timer *timer);
void kmscon_timer_unref(struct kmscon_timer *timer);
int kmscon_eloop_new_timer(struct kmscon_eloop *loop, struct kmscon_timer **out,
const struct itimerspec *spec, kmscon_timer_cb cb, void *data);
int kmscon_eloop_add_timer(struct kmscon_eloop *loop,
struct kmscon_timer *timer, const struct itimerspec *spec,
kmscon_timer_cb cb, void *data);
void kmscon_eloop_rm_timer(struct kmscon_timer *timer);
int kmscon_eloop_update_timer(struct kmscon_timer *timer,
const struct itimerspec *spec);
#endif /* KMSCON_ELOOP_H */