From aeb5222a6d7f7e9be5b737e2dbaf71185a9946db Mon Sep 17 00:00:00 2001 From: =?utf8?q?P=C3=A1draig=20Brady?= Date: Mon, 18 Jul 2011 10:49:17 +0100 Subject: [PATCH] timeout: support sub-second timeouts * src/timeout.c (settimeout): A new function to convert from a floating point duration and call alarm() or timer_settime() if that's available. (parse_duration): Return a double rather than unsigned int. (usage): Mention floating point is supported. (main): Pass the double to settimeout() rather than calling alarm() directly with the parsed int. (cleanup): Likewise. * doc/coreutils.texi (timeout invocation): Say floating point timeouts now supported, and mention the caveat with resolution. * bootstrap.conf: Include the timer-time gnulib module. * tests/misc/timeout-parameters: Add a test with nanoseconds. * NEWS: Mention the improvement. --- NEWS | 2 ++ bootstrap.conf | 1 + doc/coreutils.texi | 4 ++- src/Makefile.am | 1 + src/timeout.c | 70 +++++++++++++++++++++++++++++++------------ tests/misc/timeout-parameters | 3 ++ 6 files changed, 61 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 07207196c..fb444f605 100644 --- a/NEWS +++ b/NEWS @@ -59,6 +59,8 @@ GNU coreutils NEWS -*- outline -*- stat -f now recognizes the GPFS, MQUEUE and PSTOREFS file system types. + timeout now supports sub-second timeouts. + ** Build-related Changes inherited from gnulib address a build failure on HP-UX 11.11 diff --git a/bootstrap.conf b/bootstrap.conf index d91bee0c8..937f7f326 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -212,6 +212,7 @@ gnulib_modules=" sys_stat sys_wait termios + timer-time timespec tzset uname diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 424446c25..b406a3cfb 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -15694,7 +15694,7 @@ or a number. Also see @xref{Signal specifications}. @end table @cindex time units -@var{duration} is an integer followed by an optional unit: +@var{duration} is a floating point number followed by an optional unit: @display @samp{s} for seconds (the default) @samp{m} for minutes @@ -15702,6 +15702,8 @@ or a number. Also see @xref{Signal specifications}. @samp{d} for days @end display A duration of 0 disables the associated timeout. +Note that the actual timeout duration is dependent on system conditions, +which should be especially considered when specifying sub-second timeouts. @cindex exit status of @command{timeout} Exit status: diff --git a/src/Makefile.am b/src/Makefile.am index ef0e7a4d3..b0b7eb527 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -329,6 +329,7 @@ date_LDADD += $(LIB_CLOCK_GETTIME) ginstall_LDADD += $(LIB_CLOCK_GETTIME) ls_LDADD += $(LIB_CLOCK_GETTIME) pr_LDADD += $(LIB_CLOCK_GETTIME) +timeout_LDADD += $(LIB_TIMER_TIME) touch_LDADD += $(LIB_CLOCK_GETTIME) # for gethrxtime diff --git a/src/timeout.c b/src/timeout.c index ccb4f85d4..6a37508fd 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -78,7 +78,7 @@ static int timed_out; static int term_signal = SIGTERM; /* same default as kill command. */ static int monitored_pid; static int sigs_to_ignore[NSIG]; /* so monitor can ignore sigs it resends. */ -static unsigned long kill_after; +static double kill_after; static bool foreground; /* whether to use another program group. */ /* for long options with no corresponding short option, use enum */ @@ -97,6 +97,50 @@ static struct option const long_options[] = {NULL, 0, NULL, 0} }; +/* Start the timeout after which we'll receive a SIGALRM. + Round DURATION up to the next representable value. + Treat out-of-range values as if they were maximal, + as that's more useful in practice than reporting an error. + '0' means don't timeout. */ +static void +settimeout (double duration) +{ +/* timer_settime() provides potentially nanosecond resolution. + setitimer() is more portable (to Darwin for example), + but only provides microsecond resolution and thus is + a little more awkward to use with timespecs, as well as being + deprecated by POSIX. Instead we fallback to single second + resolution provided by alarm(). */ + +#if HAVE_TIMER_SETTIME + struct timespec ts = dtotimespec (duration); + struct itimerspec its = { {0, 0}, ts }; + timer_t timerid; + if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0) + { + if (timer_settime (timerid, 0, &its, NULL) == 0) + return; + else + { + error (0, errno, _("warning: timer_settime")); + timer_delete (timerid); + } + } + else + error (0, errno, _("warning: timer_create")); +#endif + + unsigned int timeint; + if (UINT_MAX <= duration) + timeint = UINT_MAX; + else + { + unsigned int duration_floor = duration; + timeint = duration_floor + (duration_floor < duration); + } + alarm (timeint); +} + /* send sig to group but not ourselves. * FIXME: Is there a better way to achieve this? */ static int @@ -125,7 +169,7 @@ cleanup (int sig) { /* Start a new timeout after which we'll send SIGKILL. */ term_signal = SIGKILL; - alarm (kill_after); + settimeout (kill_after); kill_after = 0; /* Don't let later signals reset kill alarm. */ } @@ -182,7 +226,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\ fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\n\ -DURATION is an integer with an optional suffix:\n\ +DURATION is a floating point number with an optional suffix:\n\ `s' for seconds (the default), `m' for minutes, `h' for hours \ or `d' for days.\n"), stdout); @@ -232,7 +276,7 @@ apply_time_suffix (double *x, char suffix_char) return true; } -static unsigned int +static double parse_duration (const char* str) { double duration; @@ -250,19 +294,7 @@ parse_duration (const char* str) usage (EXIT_CANCELED); } - /* Return the requested duration, rounded up to the next representable value. - Treat out-of-range values as if they were maximal, - as that's more useful in practice than reporting an error. - - FIXME: Use dtotimespec + setitimer if setitimer is available, - as that has higher resolution. */ - if (UINT_MAX <= duration) - return UINT_MAX; - else - { - unsigned int duration_floor = duration; - return duration_floor + (duration_floor < duration); - } + return duration; } static void @@ -285,7 +317,7 @@ install_signal_handlers (int sigterm) int main (int argc, char **argv) { - unsigned long timeout; + double timeout; char signame[SIG2STR_MAX]; int c; @@ -373,7 +405,7 @@ main (int argc, char **argv) pid_t wait_result; int status; - alarm (timeout); + settimeout (timeout); while ((wait_result = waitpid (monitored_pid, &status, 0)) < 0 && errno == EINTR) diff --git a/tests/misc/timeout-parameters b/tests/misc/timeout-parameters index 56804a808..e34a72807 100755 --- a/tests/misc/timeout-parameters +++ b/tests/misc/timeout-parameters @@ -55,6 +55,9 @@ test $? = 0 || fail=1 timeout 2.34e+5d sleep 0 test $? = 0 || fail=1 +# nanoseconds potentially supported +timeout .999999999 sleep 0 || fail=1 + # invalid signal spec timeout --signal=invalid 1 sleep 0 test $? = 125 || fail=1 -- 2.11.4.GIT