autoupdate
[gnulib.git] / lib / nanosleep.c
index 126a026..2b5bf5a 100644 (file)
@@ -1,10 +1,12 @@
 /* Provide a replacement for the POSIX nanosleep function.
-   Copyright (C) 1999 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or modify
+   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
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   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
    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, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /* written by Jim Meyering */
 
 #include <config.h>
+
+#include <time.h>
+
+#include "intprops.h"
+#include "sig-handler.h"
+#include "verify.h"
+
+#include <stdbool.h>
 #include <stdio.h>
 #include <sys/types.h>
+#include <sys/select.h>
 #include <signal.h>
 
+#include <sys/time.h>
 #include <errno.h>
-#ifndef errno
-extern int errno;
-#endif
 
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
 
-#include <time.h>
-/* FIXME: is including both like this kosher?  */
-#include <sys/time.h>
+#undef nanosleep
 
-static int suspended;
+enum { BILLION = 1000 * 1000 * 1000 };
 
-/* Handle SIGCONT. */
+#if HAVE_BUG_BIG_NANOSLEEP
 
-static void
-sighandler (int sig)
+int
+rpl_nanosleep (const struct timespec *requested_delay,
+              struct timespec *remaining_delay)
 {
-#ifdef SA_INTERRUPT
-  struct sigaction sigact;
+  /* nanosleep mishandles large sleeps due to internal overflow
+     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 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);
+}
 
-  sigact.sa_handler = SIG_DFL;
-  sigemptyset (&sigact.sa_mask);
-  sigact.sa_flags = 0;
-  sigaction (sig, &sigact, NULL);
 #else
-  signal (sig, SIG_DFL);
-#endif
 
+/* Some systems (MSDOS) don't have SIGCONT.
+   Using SIGTERM here turns the signal-handling code below
+   into a no-op on such systems. */
+# ifndef SIGCONT
+#  define SIGCONT SIGTERM
+# endif
+
+static sig_atomic_t volatile suspended;
+
+/* Handle SIGCONT. */
+
+static void
+sighandler (int sig)
+{
   suspended = 1;
-  kill (getpid (), sig);
 }
 
-/* Sleep for USEC microseconds. */
+/* Suspend execution for at least *TS_DELAY seconds.  */
 
 static void
 my_usleep (const struct timespec *ts_delay)
 {
   struct timeval tv_delay;
   tv_delay.tv_sec = ts_delay->tv_sec;
-  tv_delay.tv_usec = 1000 * ts_delay->tv_nsec;
-  select (0, (void *) 0, (void *) 0, (void *) 0, &tv_delay);
+  tv_delay.tv_usec = (ts_delay->tv_nsec + 999) / 1000;
+  if (tv_delay.tv_usec == 1000000)
+    {
+      time_t t1 = tv_delay.tv_sec + 1;
+      if (t1 < tv_delay.tv_sec)
+       tv_delay.tv_usec = 1000000 - 1; /* close enough */
+      else
+       {
+         tv_delay.tv_sec = t1;
+         tv_delay.tv_usec = 0;
+       }
+    }
+  select (0, NULL, NULL, NULL, &tv_delay);
 }
 
+/* Suspend execution for at least *REQUESTED_DELAY seconds.  The
+   *REMAINING_DELAY part isn't implemented yet.  */
+
 int
-nanosleep (const struct timespec *requested_delay,
-          struct timespec *remaining_delay)
+rpl_nanosleep (const struct timespec *requested_delay,
+              struct timespec *remaining_delay)
 {
-#ifdef SA_INTERRUPT
-  struct sigaction oldact, newact;
-#endif
+  static bool initialized;
 
-  suspended = 0;
+  if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
+    {
+      errno = EINVAL;
+      return -1;
+    }
 
-  /* set up sig handler -- but maybe only do this the first time?  */
-#ifdef SA_INTERRUPT
-  newact.sa_handler = sighandler;
-  sigemptyset (&newact.sa_mask);
-  newact.sa_flags = 0;
+  /* set up sig handler */
+  if (! initialized)
+    {
+      struct sigaction oldact;
+
+      sigaction (SIGCONT, NULL, &oldact);
+      if (get_handler (&oldact) != SIG_IGN)
+       {
+         struct sigaction newact;
+
+         newact.sa_handler = sighandler;
+         sigemptyset (&newact.sa_mask);
+         newact.sa_flags = 0;
+         sigaction (SIGCONT, &newact, NULL);
+       }
+      initialized = true;
+    }
 
-  sigaction (SIGCONT, NULL, &oldact);
-  if (oldact.sa_handler != SIG_IGN)
-    sigaction (SIGCONT, &newact, NULL);
-#else
-  if (signal (SIGCONT, SIG_IGN) != SIG_IGN)
-    signal (SIGCONT, sighandler);
-#endif
+  suspended = 0;
 
   my_usleep (requested_delay);
 
@@ -99,11 +167,12 @@ nanosleep (const struct timespec *requested_delay,
       /* Calculate time remaining.  */
       /* FIXME: the code in sleep doesn't use this, so there's no
         rush to implement it.  */
+
+      errno = EINTR;
     }
 
   /* FIXME: Restore sig handler?  */
 
-  errno = EINTR;
-
   return suspended;
 }
+#endif