From a905b35027f406a031a844995b32b7b9291d0336 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 18 Nov 2009 20:10:42 -0700 Subject: [PATCH] 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 --- ChangeLog | 14 ++++++ doc/posix-functions/nanosleep.texi | 3 ++ lib/nanosleep.c | 82 +++++++++++++++------------------ m4/nanosleep.m4 | 9 +--- modules/nanosleep | 4 +- modules/nanosleep-tests | 12 +++++ tests/test-nanosleep.c | 94 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 162 insertions(+), 56 deletions(-) create mode 100644 modules/nanosleep-tests create mode 100644 tests/test-nanosleep.c diff --git a/ChangeLog b/ChangeLog index 23aa5749e..10f2c7453 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2009-11-20 Eric Blake + 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. diff --git a/doc/posix-functions/nanosleep.texi b/doc/posix-functions/nanosleep.texi index a44ffa734..f4f0b565d 100644 --- a/doc/posix-functions/nanosleep.texi +++ b/doc/posix-functions/nanosleep.texi @@ -19,6 +19,9 @@ AIX 4.3.2. 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: diff --git a/lib/nanosleep.c b/lib/nanosleep.c index 7a7cc02fd..a9311f84d 100644 --- 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 +#include "intprops.h" #include "sig-handler.h" -#include "timespec.h" +#include "verify.h" #include #include @@ -42,56 +43,45 @@ enum { BILLION = 1000 * 1000 * 1000 }; #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 (;;) + 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; + + while (limit < seconds) { - int r = nanosleep (&delay, remaining_delay); - if (r == 0) - { - time_t secs_sofar; - struct timespec now; - getnow (&now); - - 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++; - } - } + 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 diff --git a/m4/nanosleep.m4 b/m4/nanosleep.m4 index d991b6121..211b367a0 100644 --- 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_DEFUN([gl_FUNC_NANOSLEEP], 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 @@ AC_DEFUN([gl_FUNC_NANOSLEEP], 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 diff --git a/modules/nanosleep b/modules/nanosleep index a652e53c2..d457b6f04 100644 --- a/modules/nanosleep +++ b/modules/nanosleep @@ -6,9 +6,8 @@ lib/nanosleep.c m4/nanosleep.m4 Depends-on: -clock-time extensions -gettime +intprops multiarch select sigaction @@ -16,6 +15,7 @@ stdbool sys_select sys_time time +verify configure.ac: gl_FUNC_NANOSLEEP diff --git a/modules/nanosleep-tests b/modules/nanosleep-tests new file mode 100644 index 000000000..67e6d4ee8 --- /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) diff --git a/tests/test-nanosleep.c b/tests/test-nanosleep.c new file mode 100644 index 000000000..eb4bef67a --- /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 . */ + +/* Written by Eric Blake , 2009. */ + +#include + +#include + +#include +#include +#include +#include +#include + +#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; +} -- 2.11.0