Skip to content

Commit

Permalink
Handle different clock IDs in timerfd_create
Browse files Browse the repository at this point in the history
Also handle TFD_TIMER_ABSTIME in timerfd_settime.

Fix ish-app#591
  • Loading branch information
tbodt committed Jan 4, 2020
1 parent b9e249b commit 8ff84f2
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 15 deletions.
6 changes: 3 additions & 3 deletions kernel/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ dword_t sys_utimensat(fd_t at_f, addr_t path_addr, addr_t times_addr, dword_t fl
struct timespec atime;
struct timespec mtime;
if (times_addr == 0) {
atime = mtime = timespec_now();
atime = mtime = timespec_now(CLOCK_REALTIME);
} else {
struct timespec_ times[2];
if (user_get(times_addr, times))
Expand All @@ -671,7 +671,7 @@ dword_t sys_utimes(addr_t path_addr, addr_t times_addr) {
struct timespec atime;
struct timespec mtime;
if (times_addr == 0) {
atime = mtime = timespec_now();
atime = mtime = timespec_now(CLOCK_REALTIME);
} else {
struct timeval_ times[2];
if (user_get(times_addr, times))
Expand All @@ -686,7 +686,7 @@ dword_t sys_utime(addr_t path_addr, addr_t times_addr) {
struct timespec atime;
struct timespec mtime;
if (times_addr == 0) {
atime = mtime = timespec_now();
atime = mtime = timespec_now(CLOCK_REALTIME);
} else {
struct utimbuf_ {
time_t_ actime;
Expand Down
22 changes: 17 additions & 5 deletions kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "kernel/calls.h"
#include "kernel/errno.h"
#include "kernel/resource.h"
#include "kernel/time.h"
#include "fs/poll.h"

dword_t sys_time(addr_t time_out) {
Expand Down Expand Up @@ -91,7 +92,7 @@ static int itimer_set(struct tgroup *group, int which, struct timer_spec spec, s
}

if (!group->timer) {
struct timer *timer = timer_new((timer_callback_t) itimer_notify, current);
struct timer *timer = timer_new(CLOCK_REALTIME, (timer_callback_t) itimer_notify, current);
if (IS_ERR(timer))
return PTR_ERR(timer);
group->timer = timer;
Expand Down Expand Up @@ -228,21 +229,27 @@ static void timerfd_callback(struct fd *fd) {

fd_t sys_timerfd_create(int_t clockid, int_t flags) {
STRACE("timerfd_create(%d, %#x)", clockid, flags);
if (clockid != ITIMER_REAL_) {
FIXME("timerfd %d", clockid);
return _EINVAL;
clockid_t real_clockid;
switch (clockid) {
case CLOCK_REALTIME_: real_clockid = CLOCK_REALTIME; break;
case CLOCK_MONOTONIC_: real_clockid = CLOCK_MONOTONIC; break;
default: FIXME("timerfd %d", clockid); return _EINVAL;
}

struct fd *fd = adhoc_fd_create(&timerfd_ops);
if (fd == NULL)
return _ENOMEM;

fd->timerfd.timer = timer_new((timer_callback_t) timerfd_callback, fd);
fd->timerfd.timer = timer_new(real_clockid, (timer_callback_t) timerfd_callback, fd);
return f_install(fd, flags);
}

#define TFD_TIMER_ABSTIME_ (1 << 0)

int_t sys_timerfd_settime(fd_t f, int_t flags, addr_t new_value_addr, addr_t old_value_addr) {
STRACE("timerfd_settime(%d, %d, %#x, %#x)", f, flags, new_value_addr, old_value_addr);
if (flags & ~(TFD_TIMER_ABSTIME_))
return _EINVAL;
struct fd *fd = f_get(f);
if (fd == NULL)
return _EBADF;
Expand All @@ -259,6 +266,11 @@ int_t sys_timerfd_settime(fd_t f, int_t flags, addr_t new_value_addr, addr_t old
.interval.tv_nsec = value.interval.nsec,
};
struct timer_spec old_spec;
if (flags & TFD_TIMER_ABSTIME_) {
struct timespec now = timespec_now(fd->timerfd.timer->clockid);
spec.value = timespec_subtract(spec.value, now);
}

lock(&fd->lock);
int err = timer_set(fd->timerfd.timer, spec, &old_spec);
unlock(&fd->lock);
Expand Down
9 changes: 5 additions & 4 deletions util/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
#include "util/timer.h"
#include "misc.h"

struct timer *timer_new(timer_callback_t callback, void *data) {
struct timer *timer_new(clockid_t clockid, timer_callback_t callback, void *data) {
struct timer *timer = malloc(sizeof(struct timer));
timer->clockid = clockid;
timer->callback = callback;
timer->data = data;
timer->running = false;
Expand All @@ -32,12 +33,12 @@ static void *timer_thread(void *param) {
struct timer *timer = param;
lock(&timer->lock);
while (true) {
struct timespec remaining = timespec_subtract(timer->end, timespec_now());
struct timespec remaining = timespec_subtract(timer->end, timespec_now(timer->clockid));
while (timer->running && timespec_positive(remaining)) {
unlock(&timer->lock);
nanosleep(&remaining, NULL);
lock(&timer->lock);
remaining = timespec_subtract(timer->end, timespec_now());
remaining = timespec_subtract(timer->end, timespec_now(timer->clockid));
}
if (timer->running)
timer->callback(timer->data);
Expand All @@ -58,7 +59,7 @@ static void *timer_thread(void *param) {

int timer_set(struct timer *timer, struct timer_spec spec, struct timer_spec *oldspec) {
lock(&timer->lock);
struct timespec now = timespec_now();
struct timespec now = timespec_now(timer->clockid);
if (oldspec != NULL) {
oldspec->value = timespec_subtract(timer->end, now);
oldspec->interval = timer->interval;
Expand Down
9 changes: 6 additions & 3 deletions util/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
#include <assert.h>
#include "util/sync.h"

static inline struct timespec timespec_now() {
static inline struct timespec timespec_now(clockid_t clockid) {
assert(clockid == CLOCK_MONOTONIC || clockid == CLOCK_REALTIME);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); // can't fail, according to posix spec
clock_gettime(clockid, &now); // can't fail, according to posix spec
return now;
}

Expand Down Expand Up @@ -43,6 +45,7 @@ static inline bool timespec_positive(struct timespec ts) {

typedef void (*timer_callback_t)(void *data);
struct timer {
clockid_t clockid;
struct timespec start;
struct timespec end;
struct timespec interval;
Expand All @@ -55,7 +58,7 @@ struct timer {
bool dead;
};

struct timer *timer_new(timer_callback_t callback, void *data);
struct timer *timer_new(clockid_t clockid, timer_callback_t callback, void *data);
void timer_free(struct timer *timer);
// value is how long to wait until the next fire
// interval is how long after that to wait until the next fire (if non-zero)
Expand Down

0 comments on commit 8ff84f2

Please sign in to comment.