Mercurial > hg > octave-lojdl > gnulib-hg
changeset 12327:9e07d6d6ed00
nanosleep: work around cygwin bug
Cygwin 1.5.x mistakenly failed with EINVAL for a duration longer
than 49.7 days (2**32 milliseconds). Meanwhile, the existing
code for HAVE_BUG_BIG_NANOSLEEP would infloop, instead of return
failure, for invalid arguments.
* lib/nanosleep.c (rpl_nanosleep) [HAVE_BUG_BIG_NANOSLEEP]:
Fix logic bug when nanosleep fails. Work around cygwin 1.5.x
bug.
(getnow): Delete, not needed.
* m4/nanosleep.m4 (gl_FUNC_NANOSLEEP): No longer require
LIB_CLOCK_GETTIME.
* modules/nanosleep (Depends-on): Add intprops and verify. Drop
clock-time, gettime.
* doc/posix-functions/nanosleep.texi (nanosleep): Document the
bug.
* modules/nanosleep-tests: New test.
* tests/test-nanosleep.c: New file.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Wed, 18 Nov 2009 20:10:42 -0700 |
parents | 25e64e77bb53 |
children | bf3fd58aaf19 |
files | ChangeLog doc/posix-functions/nanosleep.texi lib/nanosleep.c m4/nanosleep.m4 modules/nanosleep modules/nanosleep-tests tests/test-nanosleep.c |
diffstat | 7 files changed, 162 insertions(+), 56 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2009-11-20 Eric Blake <ebb9@byu.net> + nanosleep: work around cygwin bug + * lib/nanosleep.c (rpl_nanosleep) [HAVE_BUG_BIG_NANOSLEEP]: + Fix logic bug when nanosleep fails. Work around cygwin 1.5.x + bug. + (getnow): Delete, not needed. + * m4/nanosleep.m4 (gl_FUNC_NANOSLEEP): No longer require + LIB_CLOCK_GETTIME. + * modules/nanosleep (Depends-on): Add intprops and verify. Drop + clock-time, gettime. + * doc/posix-functions/nanosleep.texi (nanosleep): Document the + bug. + * modules/nanosleep-tests: New test. + * tests/test-nanosleep.c: New file. + sleep: work around cygwin bug * lib/sleep.c (rpl_sleep): Work around the bug. * m4/sleep.m4 (gl_FUNC_SLEEP): Detect the bug.
--- a/doc/posix-functions/nanosleep.texi +++ b/doc/posix-functions/nanosleep.texi @@ -19,6 +19,9 @@ This function mishandles large arguments when interrupted by a signal on some platforms: Linux 64-bit, Solaris 64-bit. +@item +This function cannot sleep longer than 49.7 days on some platforms: +Cygwin 1.5.x. @end itemize Portability problems not fixed by Gnulib:
--- a/lib/nanosleep.c +++ b/lib/nanosleep.c @@ -1,7 +1,7 @@ /* Provide a replacement for the POSIX nanosleep function. - Copyright (C) 1999, 2000, 2002, 2004, 2005, 2006, 2007, 2008 Free - Software Foundation, Inc. + Copyright (C) 1999, 2000, 2002, 2004, 2005, 2006, 2007, 2008, 2009 + 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 @@ -22,8 +22,9 @@ #include <time.h> +#include "intprops.h" #include "sig-handler.h" -#include "timespec.h" +#include "verify.h" #include <stdbool.h> #include <stdio.h> @@ -42,56 +43,45 @@ #if HAVE_BUG_BIG_NANOSLEEP -static void -getnow (struct timespec *t) -{ -# if defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME - if (clock_gettime (CLOCK_MONOTONIC, t) == 0) - return; -# endif - gettime (t); -} - int rpl_nanosleep (const struct timespec *requested_delay, struct timespec *remaining_delay) { /* nanosleep mishandles large sleeps due to internal overflow - problems, so check that the proper amount of time has actually - elapsed. */ - - struct timespec delay = *requested_delay; - struct timespec t0; - getnow (&t0); - - for (;;) - { - int r = nanosleep (&delay, remaining_delay); - if (r == 0) - { - time_t secs_sofar; - struct timespec now; - getnow (&now); + problems. The worst known case of this is cygwin 1.5.x, which + can't sleep more than 49.7 days (2**32 milliseconds). Solve this + by breaking the sleep up into smaller chunks. Verify that time_t + is large enough. */ + verify (TYPE_MAXIMUM (time_t) / 49 / 24 / 60 / 60); + const time_t limit = 49 * 24 * 60 * 60; + time_t seconds = requested_delay->tv_sec; + struct timespec intermediate; + intermediate.tv_nsec = 0; - secs_sofar = now.tv_sec - t0.tv_sec; - if (requested_delay->tv_sec < secs_sofar) - return 0; - delay.tv_sec = requested_delay->tv_sec - secs_sofar; - delay.tv_nsec = requested_delay->tv_nsec - (now.tv_nsec - t0.tv_nsec); - if (delay.tv_nsec < 0) - { - if (delay.tv_sec == 0) - return 0; - delay.tv_nsec += BILLION; - delay.tv_sec--; - } - else if (BILLION <= delay.tv_nsec) - { - delay.tv_nsec -= BILLION; - delay.tv_sec++; - } - } + while (limit < seconds) + { + int result; + intermediate.tv_sec = limit; + result = nanosleep (&intermediate, remaining_delay); + seconds -= limit; + if (result) + { + if (remaining_delay) + { + remaining_delay->tv_sec += seconds; + remaining_delay->tv_nsec += requested_delay->tv_nsec; + if (BILLION <= requested_delay->tv_nsec) + { + remaining_delay->tv_sec++; + remaining_delay->tv_nsec -= BILLION; + } + } + return result; + } } + intermediate.tv_sec = seconds; + intermediate.tv_nsec = requested_delay->tv_nsec; + return nanosleep (&intermediate, remaining_delay); } #else
--- a/m4/nanosleep.m4 +++ b/m4/nanosleep.m4 @@ -1,4 +1,4 @@ -# serial 28 +# serial 29 dnl From Jim Meyering. dnl Check for the nanosleep function. @@ -17,7 +17,6 @@ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS]) - AC_REQUIRE([gl_CLOCK_TIME]) AC_CHECK_HEADERS_ONCE([sys/time.h]) nanosleep_save_libs=$LIBS @@ -103,12 +102,6 @@ if test "$gl_cv_func_nanosleep" = 'no (mishandles large arguments)'; then AC_DEFINE([HAVE_BUG_BIG_NANOSLEEP], [1], [Define to 1 if nanosleep mishandles large arguments.]) - for ac_lib in $LIB_CLOCK_GETTIME; do - case " $LIB_NANOSLEEP " in - *" $ac_lib "*) ;; - *) LIB_NANOSLEEP="$LIB_NANOSLEEP $ac_lib";; - esac - done fi AC_LIBOBJ([nanosleep]) gl_PREREQ_NANOSLEEP
--- a/modules/nanosleep +++ b/modules/nanosleep @@ -6,9 +6,8 @@ m4/nanosleep.m4 Depends-on: -clock-time extensions -gettime +intprops multiarch select sigaction @@ -16,6 +15,7 @@ sys_select sys_time time +verify configure.ac: gl_FUNC_NANOSLEEP
new file mode 100644 --- /dev/null +++ b/modules/nanosleep-tests @@ -0,0 +1,12 @@ +Files: +tests/test-nanosleep.c + +Depends-on: + +configure.ac: +AC_CHECK_DECLS_ONCE([alarm]) + +Makefile.am: +TESTS += test-nanosleep +check_PROGRAMS += test-nanosleep +test_nanosleep_LDADD = $(LDADD) $(LIB_NANOSLEEP)
new file mode 100644 --- /dev/null +++ b/tests/test-nanosleep.c @@ -0,0 +1,94 @@ +/* Test of nanosleep() function. + Copyright (C) 2009 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Eric Blake <ebb9@byu.net>, 2009. */ + +#include <config.h> + +#include <time.h> + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "intprops.h" + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +#if HAVE_DECL_ALARM +static void +handle_alarm (int sig) +{ + if (sig != SIGALRM) + _exit (1); +} +#endif + +int +main() +{ + struct timespec ts; + + ts.tv_sec = 1000; + ts.tv_nsec = -1; + errno = 0; + ASSERT (nanosleep (&ts, NULL) == -1); + ASSERT (errno == EINVAL); + ts.tv_nsec = 1000000000; + errno = 0; + ASSERT (nanosleep (&ts, NULL) == -1); + ASSERT (errno == EINVAL); + + ts.tv_sec = 0; + ts.tv_nsec = 1; + ASSERT (nanosleep (&ts, &ts) == 0); + /* Remaining time is only defined on EINTR failure; but on success, + it is typically either 0 or unchanged from input. At any rate, + it shouldn't be randomly changed to unrelated values. */ + ASSERT (ts.tv_sec == 0); + ASSERT (ts.tv_nsec == 0 || ts.tv_nsec == 1); + ts.tv_nsec = 0; + ASSERT (nanosleep (&ts, NULL) == 0); + +#if HAVE_DECL_ALARM + { + const time_t pentecost = 50 * 24 * 60 * 60; /* 50 days. */ + signal (SIGALRM, handle_alarm); + alarm (1); + ts.tv_sec = pentecost; + ts.tv_nsec = 999999999; + errno = 0; + ASSERT (nanosleep (&ts, &ts) == -1); + ASSERT (errno == EINTR); + ASSERT (pentecost - 10 < ts.tv_sec && ts.tv_sec <= pentecost); + ASSERT (0 <= ts.tv_nsec && ts.tv_nsec <= 999999999); + } +#endif + + return 0; +}