2015-03-06 06:17:20 -06:00
|
|
|
/*
|
2020-10-06 08:03:45 -05:00
|
|
|
* No copyright is claimed. This code is in the public domain; do with
|
|
|
|
* it what you wish.
|
|
|
|
*
|
2015-03-06 06:17:20 -06:00
|
|
|
* 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"
|
|
|
|
|
2017-04-28 06:25:41 -05:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-10-24 05:28:13 -05:00
|
|
|
#ifdef HAVE_TIMER_CREATE
|
|
|
|
int setup_timer(struct ul_timer *timer,
|
|
|
|
struct itimerval *timeout,
|
2015-03-06 06:17:20 -06:00
|
|
|
void (*timeout_handler)(int, siginfo_t *, void *))
|
|
|
|
{
|
lib/timer.c: prevent pathological race condition
flock(1) uses the timer facility to interrupt a blocking flock(2)
call. However, in a pathological case (or with a sufficiently short
timeout), the timer may fire and the signal be delivered after the
timer is set up, but before we get around to doing the flock(2)
call. In that case, we'd block forever. Checking timeout_expired right
before calling flock(2) does not eliminate that race, so the only
option is to make the timer fire repeatedly. Having the timer fire
after we've returned from flock(2) is not a problem, since we only
check timeout_expired in case of EINTR (also, this firing after return
could also happen with the current code).
There is currently one other user of setup_timer (misc-utils/uuidd.c),
but in that case the signal handler simply exits. Future users of
setup_timer obviously need to ensure that they can tolerate multiple
signal deliveries.
Choosing 1% of the initial timeout as the repeating interval is
somewhat arbitrary. However, I put a lower bound of 0.01s, since
setting the interval much smaller than this may end up effectively
live-locking the process, handling a never-ending stream of signals.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
2017-04-26 06:03:10 -05:00
|
|
|
time_t sec = timeout->it_value.tv_sec;
|
|
|
|
long usec = timeout->it_value.tv_usec;
|
2015-03-06 06:17:20 -06:00
|
|
|
struct sigaction sig_a;
|
|
|
|
static struct sigevent sig_e = {
|
|
|
|
.sigev_notify = SIGEV_SIGNAL,
|
|
|
|
.sigev_signo = SIGALRM
|
|
|
|
};
|
|
|
|
struct itimerspec val = {
|
lib/timer.c: prevent pathological race condition
flock(1) uses the timer facility to interrupt a blocking flock(2)
call. However, in a pathological case (or with a sufficiently short
timeout), the timer may fire and the signal be delivered after the
timer is set up, but before we get around to doing the flock(2)
call. In that case, we'd block forever. Checking timeout_expired right
before calling flock(2) does not eliminate that race, so the only
option is to make the timer fire repeatedly. Having the timer fire
after we've returned from flock(2) is not a problem, since we only
check timeout_expired in case of EINTR (also, this firing after return
could also happen with the current code).
There is currently one other user of setup_timer (misc-utils/uuidd.c),
but in that case the signal handler simply exits. Future users of
setup_timer obviously need to ensure that they can tolerate multiple
signal deliveries.
Choosing 1% of the initial timeout as the repeating interval is
somewhat arbitrary. However, I put a lower bound of 0.01s, since
setting the interval much smaller than this may end up effectively
live-locking the process, handling a never-ending stream of signals.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
2017-04-26 06:03:10 -05:00
|
|
|
.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
|
2015-03-06 06:17:20 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
if (sigemptyset(&sig_a.sa_mask))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
sig_a.sa_flags = SA_SIGINFO;
|
|
|
|
sig_a.sa_sigaction = timeout_handler;
|
|
|
|
|
2017-02-11 14:23:26 -06:00
|
|
|
if (sigaction(SIGALRM, &sig_a, NULL))
|
2015-03-06 06:17:20 -06:00
|
|
|
return 1;
|
2018-10-24 05:28:13 -05:00
|
|
|
if (timer_create(CLOCK_MONOTONIC, &sig_e, &timer->t_id))
|
2015-03-06 06:17:20 -06:00
|
|
|
return 1;
|
2018-10-24 05:28:13 -05:00
|
|
|
if (timer_settime(timer->t_id, 0, &val, NULL))
|
2015-03-06 06:17:20 -06:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2018-10-24 05:28:13 -05:00
|
|
|
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));
|
2015-03-06 06:17:20 -06:00
|
|
|
|
2018-10-24 05:28:13 -05:00
|
|
|
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)
|
2015-03-06 06:17:20 -06:00
|
|
|
{
|
2018-10-24 05:28:13 -05:00
|
|
|
setitimer(ITIMER_REAL, &timer->old_timer, NULL);
|
|
|
|
sigaction(SIGALRM, &timer->old_sa, NULL);
|
|
|
|
|
2015-03-06 06:17:20 -06:00
|
|
|
}
|
2018-10-24 05:28:13 -05:00
|
|
|
#endif /* !HAVE_TIMER_CREATE */
|