summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--doc/posix-functions/nanosleep.texi3
-rw-r--r--lib/nanosleep.c82
-rw-r--r--m4/nanosleep.m49
-rw-r--r--modules/nanosleep4
-rw-r--r--modules/nanosleep-tests12
-rw-r--r--tests/test-nanosleep.c94
7 files changed, 162 insertions, 56 deletions
diff --git a/ChangeLog b/ChangeLog
index 23aa5749e4..10f2c74539 100644
--- 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.
diff --git a/doc/posix-functions/nanosleep.texi b/doc/posix-functions/nanosleep.texi
index a44ffa734e..f4f0b565d0 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 7a7cc02fd9..a9311f84df 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 <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 @@ 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 d991b6121c..211b367a01 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 a652e53c2c..d457b6f049 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 0000000000..67e6d4ee83
--- /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 0000000000..eb4bef67a3
--- /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;
+}