changeset 12326:25e64e77bb53

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 <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Wed, 18 Nov 2009 20:07:44 -0700
parents 444e305ec8cc
children 9e07d6d6ed00
files ChangeLog doc/posix-functions/sleep.texi lib/sleep.c lib/unistd.in.h m4/sleep.m4 m4/unistd_h.m4 modules/sleep modules/sleep-tests modules/unistd tests/test-sleep.c
diffstat 10 files changed, 120 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2009-11-20  Eric Blake  <ebb9@byu.net>
+
+	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  <meyering@redhat.com>
 
 	maint.mk: improve sc_prohibit_magic_number_exit
--- a/doc/posix-functions/sleep.texi
+++ b/doc/posix-functions/sleep.texi
@@ -15,6 +15,9 @@
 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:
--- 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 <bruno@clisp.org>, 2007.
 
    This program is free software: you can redistribute it and/or modify
@@ -20,6 +20,10 @@
 /* Specification.  */
 #include <unistd.h>
 
+#include <limits.h>
+
+#include "verify.h"
+
 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
 
 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
@@ -39,7 +43,32 @@
   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."
 
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -714,11 +714,15 @@
 
 
 #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
    <http://www.opengroup.org/susv3xsh/sleep.html>.  */
-# if !@HAVE_SLEEP@
+# if !@HAVE_SLEEP@ || @REPLACE_SLEEP@
 extern unsigned int sleep (unsigned int n);
 # endif
 #elif defined GNULIB_POSIXCHECK
--- 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 @@
   dnl it takes the number of milliseconds as argument and returns void.
   dnl mingw does not declare this function.
   AC_CHECK_DECLS([sleep], , , [#include <unistd.h>])
+  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 <errno.h>
+#include <unistd.h>
+#include <signal.h>
+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], [:])
--- 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 @@
   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])
--- a/modules/sleep
+++ b/modules/sleep
@@ -6,7 +6,9 @@
 m4/sleep.m4
 
 Depends-on:
+stdint
 unistd
+verify
 
 configure.ac:
 gl_FUNC_SLEEP
--- a/modules/sleep-tests
+++ b/modules/sleep-tests
@@ -4,8 +4,8 @@
 Depends-on:
 
 configure.ac:
+AC_CHECK_DECLS_ONCE([alarm])
 
 Makefile.am:
 TESTS += test-sleep
 check_PROGRAMS += test-sleep
-
--- a/modules/unistd
+++ b/modules/unistd
@@ -105,6 +105,7 @@
 	      -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' \
--- 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 <unistd.h>
 
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #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 @@
 
   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;
 }
-