From: Eric Blake Date: Thu, 19 Nov 2009 03:07:44 +0000 (-0700) Subject: sleep: work around cygwin bug X-Git-Tag: v0.1~5161 X-Git-Url: http://erislabs.org.uk/gitweb/?a=commitdiff_plain;h=6baa46639c17d7fba1d6aa1a3dd8ed536b7b2dc9;p=gnulib.git sleep: work around cygwin bug On cygwin 1.5.x, sleep amounts larger than 49.7 days (2**32 milliseconds) failed instantly, but with a garbage return value from uninitialized memory. * lib/sleep.c (rpl_sleep): Work around the bug. * m4/sleep.m4 (gl_FUNC_SLEEP): Detect the bug. (gl_PREREQ_SLEEP): Delete unused macro. * modules/sleep (Depends-on): Add verify. * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Add default. * modules/unistd (Makefile.am): Substitute witness. * lib/unistd.in.h (sleep): Update prototype. * doc/posix-functions/sleep.texi (sleep): Document the bug. * tests/test-sleep.c (main) [HAVE_DECL_ALARM]: Test it. * modules/sleep-tests (Depends-on): Check for alarm. Signed-off-by: Eric Blake --- diff --git a/ChangeLog b/ChangeLog index 51fe8ed2c..23aa5749e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2009-11-20 Eric Blake + + sleep: work around cygwin bug + * lib/sleep.c (rpl_sleep): Work around the bug. + * m4/sleep.m4 (gl_FUNC_SLEEP): Detect the bug. + (gl_PREREQ_SLEEP): Delete unused macro. + * modules/sleep (Depends-on): Add verify. + * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Add default. + * modules/unistd (Makefile.am): Substitute witness. + * lib/unistd.in.h (sleep): Update prototype. + * doc/posix-functions/sleep.texi (sleep): Document the bug. + * tests/test-sleep.c (main) [HAVE_DECL_ALARM]: Test it. + * modules/sleep-tests (Depends-on): Check for alarm. + 2009-11-20 Jim Meyering maint.mk: improve sc_prohibit_magic_number_exit diff --git a/doc/posix-functions/sleep.texi b/doc/posix-functions/sleep.texi index 9a7a74f0b..6df869384 100644 --- a/doc/posix-functions/sleep.texi +++ b/doc/posix-functions/sleep.texi @@ -15,6 +15,9 @@ mingw (2005 or newer). This function takes milliseconds as argument and returns @code{void} on some platforms: mingw (2005 and older). +@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/sleep.c b/lib/sleep.c index 9c56b9b81..90b482c14 100644 --- a/lib/sleep.c +++ b/lib/sleep.c @@ -1,5 +1,5 @@ /* Pausing execution of the current thread. - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007, 2009 Free Software Foundation, Inc. Written by Bruno Haible , 2007. This program is free software: you can redistribute it and/or modify @@ -20,6 +20,10 @@ /* Specification. */ #include +#include + +#include "verify.h" + #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ # define WIN32_LEAN_AND_MEAN /* avoid including junk */ @@ -39,7 +43,32 @@ sleep (unsigned int seconds) return remaining; } -#else +#elif HAVE_SLEEP + +# undef sleep + +/* Guarantee unlimited sleep and a reasonable return value. Cygwin + 1.5.x rejects attempts to sleep more than 49.7 days (2**32 + milliseconds), but uses uninitialized memory which results in a + garbage answer. */ +unsigned int +rpl_sleep (unsigned int seconds) +{ + /* This requires int larger than 16 bits. */ + verify (UINT_MAX / 49 / 24 / 60 / 60); + const unsigned int limit = 49 * 24 * 60 * 60; + while (limit < seconds) + { + unsigned int result; + seconds -= limit; + result = sleep (limit); + if (result) + return seconds + result; + } + return sleep (seconds); +} + +#else /* !HAVE_SLEEP */ #error "Please port gnulib sleep.c to your platform, possibly using usleep() or select(), then report this to bug-gnulib." diff --git a/lib/unistd.in.h b/lib/unistd.in.h index 30f7bddc2..9a9a67111 100644 --- a/lib/unistd.in.h +++ b/lib/unistd.in.h @@ -714,11 +714,15 @@ extern int rmdir (char const *name); #if @GNULIB_SLEEP@ +# if @REPLACE_SLEEP@ +# undef sleep +# define sleep rpl_sleep +# endif /* Pause the execution of the current thread for N seconds. Returns the number of seconds left to sleep. See the POSIX:2001 specification . */ -# if !@HAVE_SLEEP@ +# if !@HAVE_SLEEP@ || @REPLACE_SLEEP@ extern unsigned int sleep (unsigned int n); # endif #elif defined GNULIB_POSIXCHECK diff --git a/m4/sleep.m4 b/m4/sleep.m4 index 474ba07ba..c59b383bf 100644 --- a/m4/sleep.m4 +++ b/m4/sleep.m4 @@ -1,5 +1,5 @@ -# sleep.m4 serial 2 -dnl Copyright (C) 2007-2008 Free Software Foundation, Inc. +# sleep.m4 serial 3 +dnl Copyright (C) 2007-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -13,12 +13,37 @@ AC_DEFUN([gl_FUNC_SLEEP], dnl it takes the number of milliseconds as argument and returns void. dnl mingw does not declare this function. AC_CHECK_DECLS([sleep], , , [#include ]) + AC_CHECK_FUNCS_ONCE([sleep]) if test $ac_cv_have_decl_sleep != yes; then HAVE_SLEEP=0 AC_LIBOBJ([sleep]) - gl_PREREQ_SLEEP + else + dnl Cygwin 1.5.x has a bug where sleep can't exceed 49.7 days. + AC_CACHE_CHECK([for working sleep], [gl_cv_func_sleep_works], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +static void +handle_alarm (int sig) +{ + if (sig != SIGALRM) + _exit (2); +} +]], [[ + /* Failure to compile this test due to missing alarm is okay, + since all such platforms (mingw) also lack sleep. */ + unsigned int pentecost = 50 * 24 * 60 * 60; /* 50 days. */ + unsigned int remaining; + signal (SIGALRM, handle_alarm); + alarm (1); + remaining = sleep (pentecost); + return !(pentecost - 10 < remaining && remaining <= pentecost);]])], + [gl_cv_func_sleep_works=yes], [gl_cv_func_sleep_works=no], + [gl_cv_func_sleep_works="guessing no"])]) + if test "$gl_cv_func_sleep_works" != yes; then + REPLACE_SLEEP=1 + AC_LIBOBJ([sleep]) + fi fi ]) - -# Prerequisites of lib/sleep.c. -AC_DEFUN([gl_PREREQ_SLEEP], [:]) diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4 index 25bdb5946..0a5b5d5a7 100644 --- a/m4/unistd_h.m4 +++ b/m4/unistd_h.m4 @@ -1,4 +1,4 @@ -# unistd_h.m4 serial 35 +# unistd_h.m4 serial 36 dnl Copyright (C) 2006-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -113,6 +113,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS], REPLACE_LSEEK=0; AC_SUBST([REPLACE_LSEEK]) REPLACE_READLINK=0; AC_SUBST([REPLACE_READLINK]) REPLACE_RMDIR=0; AC_SUBST([REPLACE_RMDIR]) + REPLACE_SLEEP=0; AC_SUBST([REPLACE_SLEEP]) REPLACE_SYMLINK=0; AC_SUBST([REPLACE_SYMLINK]) REPLACE_UNLINK=0; AC_SUBST([REPLACE_UNLINK]) REPLACE_UNLINKAT=0; AC_SUBST([REPLACE_UNLINKAT]) diff --git a/modules/sleep b/modules/sleep index d8af51436..e859e59e5 100644 --- a/modules/sleep +++ b/modules/sleep @@ -6,7 +6,9 @@ lib/sleep.c m4/sleep.m4 Depends-on: +stdint unistd +verify configure.ac: gl_FUNC_SLEEP diff --git a/modules/sleep-tests b/modules/sleep-tests index 91de2eeed..0871d5117 100644 --- a/modules/sleep-tests +++ b/modules/sleep-tests @@ -4,8 +4,8 @@ tests/test-sleep.c Depends-on: configure.ac: +AC_CHECK_DECLS_ONCE([alarm]) Makefile.am: TESTS += test-sleep check_PROGRAMS += test-sleep - diff --git a/modules/unistd b/modules/unistd index 008ccdf58..1282e5380 100644 --- a/modules/unistd +++ b/modules/unistd @@ -105,6 +105,7 @@ unistd.h: unistd.in.h -e 's|@''REPLACE_LSEEK''@|$(REPLACE_LSEEK)|g' \ -e 's|@''REPLACE_READLINK''@|$(REPLACE_READLINK)|g' \ -e 's|@''REPLACE_RMDIR''@|$(REPLACE_RMDIR)|g' \ + -e 's|@''REPLACE_SLEEP''@|$(REPLACE_SLEEP)|g' \ -e 's|@''REPLACE_SYMLINK''@|$(REPLACE_SYMLINK)|g' \ -e 's|@''REPLACE_UNLINK''@|$(REPLACE_UNLINK)|g' \ -e 's|@''REPLACE_UNLINKAT''@|$(REPLACE_UNLINKAT)|g' \ diff --git a/tests/test-sleep.c b/tests/test-sleep.c index ed5a5a0c4..48abce12f 100644 --- a/tests/test-sleep.c +++ b/tests/test-sleep.c @@ -1,5 +1,5 @@ /* Test of sleep() function. - Copyright (C) 2007-2008 Free Software Foundation, Inc. + Copyright (C) 2007-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 @@ -20,21 +20,31 @@ #include +#include #include #include #define ASSERT(expr) \ - do \ - { \ - if (!(expr)) \ - { \ + do \ + { \ + if (!(expr)) \ + { \ fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ - fflush (stderr); \ - abort (); \ - } \ - } \ + fflush (stderr); \ + abort (); \ + } \ + } \ while (0) +#if HAVE_DECL_ALARM +static void +handle_alarm (int sig) +{ + if (sig != SIGALRM) + _exit (1); +} +#endif + int main() { @@ -42,6 +52,16 @@ main() ASSERT (sleep (0) == 0); +#if HAVE_DECL_ALARM + { + const unsigned int pentecost = 50 * 24 * 60 * 60; /* 50 days. */ + unsigned int remaining; + signal (SIGALRM, handle_alarm); + alarm (1); + remaining = sleep (pentecost); + ASSERT (pentecost - 10 < remaining && remaining <= pentecost); + } +#endif + return 0; } -