From 13aa6d899cf3b84f8125b555289b2616262df6f3 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 21 Feb 2005 08:10:47 +0000 Subject: [PATCH] Include xtime.h and gethrxtime.h, not xalloc.h. (timespec_subtract): Remove. (NANOSLEEP_BUG_WORKAROUND): New constant. (xnanosleep): Use gethrxtime rather than gettime; this simplifies things considerably. Use it only on GNU/Linux hosts, since the workaround shouldn't be needed elsewhere. --- lib/xnanosleep.c | 134 +++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 80 deletions(-) diff --git a/lib/xnanosleep.c b/lib/xnanosleep.c index 6ca43e560..8aa5e8a8d 100644 --- a/lib/xnanosleep.c +++ b/lib/xnanosleep.c @@ -1,5 +1,5 @@ /* xnanosleep.c -- a more convenient interface to nanosleep - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,6 +32,10 @@ #include #include +#include "timespec.h" +#include "gethrxtime.h" +#include "xtime.h" + /* The extra casts work around common compiler bugs. */ #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) /* The outer cast is needed to work around a bug in Cray C 5.0.3.0. @@ -44,44 +48,18 @@ # define TIME_T_MAX TYPE_MAXIMUM (time_t) #endif -#include "timespec.h" -#include "xalloc.h" - -/* Subtract the `struct timespec' values X and Y by computing X - Y. - If the difference is negative or zero, return false. - Otherwise, return true and store the difference in DIFF. - X and Y must have valid ts_nsec values, in the range 0 to 999999999. - If the difference would overflow, store the maximum possible difference. */ - -static bool -timespec_subtract (struct timespec *diff, - struct timespec const *x, struct timespec const *y) -{ - time_t sec = x->tv_sec - y->tv_sec; - long int nsec = x->tv_nsec - y->tv_nsec; - - if (x->tv_sec < y->tv_sec) - return false; - - if (sec < 0) - { - /* The difference has overflowed. */ - sec = TIME_T_MAX; - nsec = 999999999; - } - else if (sec == 0 && nsec <= 0) - return false; - - if (nsec < 0) - { - sec--; - nsec += 1000000000; - } - - diff->tv_sec = sec; - diff->tv_nsec = nsec; - return true; -} +/* POSIX.1-2001 requires that when a process is suspended, then + resumed, nanosleep (A, B) returns -1, sets errno to EINTR, and sets + *B to the time remaining at the point of resumption. However, some + versions of the Linux kernel incorrectly return the time remaining + at the point of suspension. Work around this bug on GNU/Linux + hosts by computing the remaining time here after nanosleep returns, + rather than by relying on nanosleep's computation. */ +#ifdef __linux__ +enum { NANOSLEEP_BUG_WORKAROUND = true }; +#else +enum { NANOSLEEP_BUG_WORKAROUND = false }; +#endif /* Sleep until the time (call it WAKE_UP_TIME) specified as SECONDS seconds after the time this function is called. @@ -93,22 +71,29 @@ timespec_subtract (struct timespec *diff, int xnanosleep (double seconds) { - bool overflow; + enum { BILLION = 1000000000 }; + + bool overflow = false; double ns; - struct timespec ts_start; struct timespec ts_sleep; - struct timespec ts_stop; + xtime_t stop = 0; assert (0 <= seconds); - if (gettime (&ts_start) != 0) - return -1; + if (NANOSLEEP_BUG_WORKAROUND) + { + xtime_t now = gethrxtime (); + double increment = XTIME_PRECISION * seconds; + xtime_t incr = increment; + stop = now + incr + (incr < increment); + overflow = (stop < now); + } /* Separate whole seconds from nanoseconds. Be careful to detect any overflow. */ ts_sleep.tv_sec = seconds; - ns = 1e9 * (seconds - ts_sleep.tv_sec); - overflow = ! (ts_sleep.tv_sec <= seconds && 0 <= ns && ns <= 1e9); + ns = BILLION * (seconds - ts_sleep.tv_sec); + overflow |= ! (ts_sleep.tv_sec <= seconds && 0 <= ns && ns <= BILLION); ts_sleep.tv_nsec = ns; /* Round up to the next whole number, if necessary, so that we @@ -119,7 +104,7 @@ xnanosleep (double seconds) ts_sleep.tv_nsec += (ts_sleep.tv_nsec < ns); /* Normalize the interval length. nanosleep requires this. */ - if (1000000000 <= ts_sleep.tv_nsec) + if (BILLION <= ts_sleep.tv_nsec) { time_t t = ts_sleep.tv_sec + 1; @@ -127,45 +112,34 @@ xnanosleep (double seconds) overflow |= (t < ts_sleep.tv_sec); ts_sleep.tv_sec = t; - ts_sleep.tv_nsec -= 1000000000; - } - - /* Compute the time until which we should sleep. */ - ts_stop.tv_sec = ts_start.tv_sec + ts_sleep.tv_sec; - ts_stop.tv_nsec = ts_start.tv_nsec + ts_sleep.tv_nsec; - if (1000000000 <= ts_stop.tv_nsec) - { - ++ts_stop.tv_sec; - ts_stop.tv_nsec -= 1000000000; + ts_sleep.tv_nsec -= BILLION; } - /* Detect integer overflow. */ - overflow |= (ts_stop.tv_sec < ts_start.tv_sec - || (ts_stop.tv_sec == ts_start.tv_sec - && ts_stop.tv_nsec < ts_start.tv_nsec)); - - if (overflow) + for (;;) { - /* Fix ts_sleep and ts_stop, which may be garbage due to overflow. */ - ts_sleep.tv_sec = ts_stop.tv_sec = TIME_T_MAX; - ts_sleep.tv_nsec = ts_stop.tv_nsec = 999999999; - } + if (overflow) + { + ts_sleep.tv_sec = TIME_T_MAX; + ts_sleep.tv_nsec = BILLION - 1; + } - while (nanosleep (&ts_sleep, NULL) != 0) - { - if (errno != EINTR || gettime (&ts_start) != 0) + if (nanosleep (&ts_sleep, NULL) == 0) + break; + if (errno != EINTR) return -1; - /* POSIX.1-2001 requires that when a process is suspended, then - resumed, nanosleep (A, B) returns -1, sets errno to EINTR, - and sets *B to the time remaining at the point of resumption. - However, some versions of the Linux kernel incorrectly return - the time remaining at the point of suspension. Work around - this bug by computing the remaining time here, rather than by - relying on nanosleep's computation. */ - - if (! timespec_subtract (&ts_sleep, &ts_stop, &ts_start)) - break; + if (NANOSLEEP_BUG_WORKAROUND) + { + xtime_t now = gethrxtime (); + if (stop <= now) + break; + else + { + xtime_t remaining = stop - now; + ts_sleep.tv_sec = xtime_nonnegative_sec (remaining); + ts_sleep.tv_nsec = xtime_nonnegative_nsec (remaining); + } + } } return 0; -- 2.34.1