diff --git a/src/eloop.c b/src/eloop.c index 2631393..4ac35fc 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -35,9 +35,11 @@ #include #include #include +#include #include #include +#include #include #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; diff --git a/src/eloop.h b/src/eloop.h index e12e0ca..8f73a33 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -34,17 +34,22 @@ #ifndef KMSCON_ELOOP_H #define KMSCON_ELOOP_H +#include #include +#include 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 */