sleep: work around cygwin bug
authorEric Blake <ebb9@byu.net>
Thu, 19 Nov 2009 03:07:44 +0000 (20:07 -0700)
committerEric Blake <ebb9@byu.net>
Fri, 20 Nov 2009 14:10:22 +0000 (07:10 -0700)
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>
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

index 51fe8ed..23aa574 100644 (file)
--- 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
index 9a7a74f..6df8693 100644 (file)
@@ -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:
index 9c56b9b..90b482c 100644 (file)
@@ -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
 /* 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 @@ 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."
 
index 30f7bdd..9a9a671 100644 (file)
@@ -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
    <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
index 474ba07..c59b383 100644 (file)
@@ -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 <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], [:])
index 25bdb59..0a5b5d5 100644 (file)
@@ -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])
index d8af514..e859e59 100644 (file)
@@ -6,7 +6,9 @@ lib/sleep.c
 m4/sleep.m4
 
 Depends-on:
+stdint
 unistd
+verify
 
 configure.ac:
 gl_FUNC_SLEEP
index 91de2ee..0871d51 100644 (file)
@@ -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
-
index 008ccdf..1282e53 100644 (file)
@@ -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' \
index ed5a5a0..48abce1 100644 (file)
@@ -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
 
 #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 @@ 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;
 }
-