diff options
Diffstat (limited to 'lib/timer.c')
-rw-r--r-- | lib/timer.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/lib/timer.c b/lib/timer.c new file mode 100644 index 0000000..cfa18f6 --- /dev/null +++ b/lib/timer.c @@ -0,0 +1,98 @@ +/* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * + * Please, don't add this file to libcommon because timers requires + * -lrt on systems with old libc (and probably also -lpthread for static + * build). + */ +#include <time.h> +#include <signal.h> +#include <sys/time.h> + +#include "c.h" +#include "timer.h" + +/* + * Note the timeout is used for the first signal, then the signal is send + * repeatedly in interval ~1% of the original timeout to avoid race in signal + * handling -- for example you want to use timer to define timeout for a + * syscall: + * + * setup_timer() + * syscall() + * cancel_timer() + * + * if the timeout is too short than it's possible that the signal is delivered + * before application enter the syscall function. For this reason timer send + * the signal repeatedly. + * + * The applications need to ensure that they can tolerate multiple signal + * deliveries. + */ +#ifdef HAVE_TIMER_CREATE +int setup_timer(struct ul_timer *timer, + struct itimerval *timeout, + void (*timeout_handler)(int, siginfo_t *, void *)) +{ + time_t sec = timeout->it_value.tv_sec; + long usec = timeout->it_value.tv_usec; + struct sigaction sig_a; + static struct sigevent sig_e = { + .sigev_notify = SIGEV_SIGNAL, + .sigev_signo = SIGALRM + }; + struct itimerspec val = { + .it_value.tv_sec = sec, + .it_value.tv_nsec = usec * 1000, + .it_interval.tv_sec = sec / 100, + .it_interval.tv_nsec = (sec ? sec % 100 : 1) * 10*1000*1000 + }; + + if (sigemptyset(&sig_a.sa_mask)) + return 1; + + sig_a.sa_flags = SA_SIGINFO; + sig_a.sa_sigaction = timeout_handler; + + if (sigaction(SIGALRM, &sig_a, NULL)) + return 1; + if (timer_create(CLOCK_MONOTONIC, &sig_e, &timer->t_id)) + return 1; + if (timer_settime(timer->t_id, 0, &val, NULL)) + return 1; + return 0; +} +void cancel_timer(struct ul_timer *timer) +{ + timer_delete(timer->t_id); +} + +#else /* !HAVE_TIMER_CREATE */ + +int setup_timer(struct ul_timer *timer, + struct itimerval *timeout, + void (*timeout_handler)(int, siginfo_t *, void *)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + memset(timer, 0, sizeof(*timer)); + + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; + sa.sa_sigaction = timeout_handler; + + if (sigaction(SIGALRM, &sa, &timer->old_sa)) + return 1; + if (setitimer(ITIMER_REAL, timeout, &timer->old_timer) != 0) + return 1; + return 0; +} + +void cancel_timer(struct ul_timer *timer) +{ + setitimer(ITIMER_REAL, &timer->old_timer, NULL); + sigaction(SIGALRM, &timer->old_sa, NULL); + +} +#endif /* !HAVE_TIMER_CREATE */ |