Blame gnulib-tests/nanosleep.c

Packit Service fdd496
/* Provide a replacement for the POSIX nanosleep function.
Packit Service fdd496
Packit Service fdd496
   Copyright (C) 1999-2000, 2002, 2004-2017 Free Software Foundation, Inc.
Packit Service fdd496
Packit Service fdd496
   This program is free software: you can redistribute it and/or modify
Packit Service fdd496
   it under the terms of the GNU General Public License as published by
Packit Service fdd496
   the Free Software Foundation; either version 3 of the License, or
Packit Service fdd496
   (at your option) any later version.
Packit Service fdd496
Packit Service fdd496
   This program is distributed in the hope that it will be useful,
Packit Service fdd496
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fdd496
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service fdd496
   GNU General Public License for more details.
Packit Service fdd496
Packit Service fdd496
   You should have received a copy of the GNU General Public License
Packit Service fdd496
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service fdd496
Packit Service fdd496
/* written by Jim Meyering
Packit Service fdd496
   and Bruno Haible for the native Windows part */
Packit Service fdd496
Packit Service fdd496
#include <config.h>
Packit Service fdd496
Packit Service fdd496
#include <time.h>
Packit Service fdd496
Packit Service fdd496
#include "intprops.h"
Packit Service fdd496
#include "sig-handler.h"
Packit Service fdd496
#include "verify.h"
Packit Service fdd496
Packit Service fdd496
#include <stdbool.h>
Packit Service fdd496
#include <stdio.h>
Packit Service fdd496
#include <sys/types.h>
Packit Service fdd496
#include <sys/select.h>
Packit Service fdd496
#include <signal.h>
Packit Service fdd496
Packit Service fdd496
#include <sys/time.h>
Packit Service fdd496
#include <errno.h>
Packit Service fdd496
Packit Service fdd496
#include <unistd.h>
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
enum { BILLION = 1000 * 1000 * 1000 };
Packit Service fdd496
Packit Service fdd496
#if HAVE_BUG_BIG_NANOSLEEP
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
nanosleep (const struct timespec *requested_delay,
Packit Service fdd496
           struct timespec *remaining_delay)
Packit Service fdd496
# undef nanosleep
Packit Service fdd496
{
Packit Service fdd496
  /* nanosleep mishandles large sleeps due to internal overflow problems.
Packit Service fdd496
     The worst known case of this is Linux 2.6.9 with glibc 2.3.4, which
Packit Service fdd496
     can't sleep more than 24.85 days (2^31 milliseconds).  Similarly,
Packit Service fdd496
     cygwin 1.5.x, which can't sleep more than 49.7 days (2^32 milliseconds).
Packit Service fdd496
     Solve this by breaking the sleep up into smaller chunks.  */
Packit Service fdd496
Packit Service fdd496
  if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
Packit Service fdd496
    {
Packit Service fdd496
      errno = EINVAL;
Packit Service fdd496
      return -1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  {
Packit Service fdd496
    /* Verify that time_t is large enough.  */
Packit Service fdd496
    verify (TYPE_MAXIMUM (time_t) / 24 / 24 / 60 / 60);
Packit Service fdd496
    const time_t limit = 24 * 24 * 60 * 60;
Packit Service fdd496
    time_t seconds = requested_delay->tv_sec;
Packit Service fdd496
    struct timespec intermediate;
Packit Service fdd496
    intermediate.tv_nsec = requested_delay->tv_nsec;
Packit Service fdd496
Packit Service fdd496
    while (limit < seconds)
Packit Service fdd496
      {
Packit Service fdd496
        int result;
Packit Service fdd496
        intermediate.tv_sec = limit;
Packit Service fdd496
        result = nanosleep (&intermediate, remaining_delay);
Packit Service fdd496
        seconds -= limit;
Packit Service fdd496
        if (result)
Packit Service fdd496
          {
Packit Service fdd496
            if (remaining_delay)
Packit Service fdd496
              remaining_delay->tv_sec += seconds;
Packit Service fdd496
            return result;
Packit Service fdd496
          }
Packit Service fdd496
        intermediate.tv_nsec = 0;
Packit Service fdd496
      }
Packit Service fdd496
    intermediate.tv_sec = seconds;
Packit Service fdd496
    return nanosleep (&intermediate, remaining_delay);
Packit Service fdd496
  }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#elif (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit Service fdd496
/* Native Windows platforms.  */
Packit Service fdd496
Packit Service fdd496
# define WIN32_LEAN_AND_MEAN
Packit Service fdd496
# include <windows.h>
Packit Service fdd496
Packit Service fdd496
/* The Windows API function Sleep() has a resolution of about 15 ms and takes
Packit Service fdd496
   at least 5 ms to execute.  We use this function for longer time periods.
Packit Service fdd496
   Additionally, we use busy-looping over short time periods, to get a
Packit Service fdd496
   resolution of about 0.01 ms.  In order to measure such short timespans,
Packit Service fdd496
   we use the QueryPerformanceCounter() function.  */
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
nanosleep (const struct timespec *requested_delay,
Packit Service fdd496
           struct timespec *remaining_delay)
Packit Service fdd496
{
Packit Service fdd496
  static bool initialized;
Packit Service fdd496
  /* Number of performance counter increments per nanosecond,
Packit Service fdd496
     or zero if it could not be determined.  */
Packit Service fdd496
  static double ticks_per_nanosecond;
Packit Service fdd496
Packit Service fdd496
  if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
Packit Service fdd496
    {
Packit Service fdd496
      errno = EINVAL;
Packit Service fdd496
      return -1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* For requested delays of one second or more, 15ms resolution is
Packit Service fdd496
     sufficient.  */
Packit Service fdd496
  if (requested_delay->tv_sec == 0)
Packit Service fdd496
    {
Packit Service fdd496
      if (!initialized)
Packit Service fdd496
        {
Packit Service fdd496
          /* Initialize ticks_per_nanosecond.  */
Packit Service fdd496
          LARGE_INTEGER ticks_per_second;
Packit Service fdd496
Packit Service fdd496
          if (QueryPerformanceFrequency (&ticks_per_second))
Packit Service fdd496
            ticks_per_nanosecond =
Packit Service fdd496
              (double) ticks_per_second.QuadPart / 1000000000.0;
Packit Service fdd496
Packit Service fdd496
          initialized = true;
Packit Service fdd496
        }
Packit Service fdd496
      if (ticks_per_nanosecond)
Packit Service fdd496
        {
Packit Service fdd496
          /* QueryPerformanceFrequency worked.  We can use
Packit Service fdd496
             QueryPerformanceCounter.  Use a combination of Sleep and
Packit Service fdd496
             busy-looping.  */
Packit Service fdd496
          /* Number of milliseconds to pass to the Sleep function.
Packit Service fdd496
             Since Sleep can take up to 8 ms less or 8 ms more than requested
Packit Service fdd496
             (or maybe more if the system is loaded), we subtract 10 ms.  */
Packit Service fdd496
          int sleep_millis = (int) requested_delay->tv_nsec / 1000000 - 10;
Packit Service fdd496
          /* Determine how many ticks to delay.  */
Packit Service fdd496
          LONGLONG wait_ticks = requested_delay->tv_nsec * ticks_per_nanosecond;
Packit Service fdd496
          /* Start.  */
Packit Service fdd496
          LARGE_INTEGER counter_before;
Packit Service fdd496
          if (QueryPerformanceCounter (&counter_before))
Packit Service fdd496
            {
Packit Service fdd496
              /* Wait until the performance counter has reached this value.
Packit Service fdd496
                 We don't need to worry about overflow, because the performance
Packit Service fdd496
                 counter is reset at reboot, and with a frequency of 3.6E6
Packit Service fdd496
                 ticks per second 63 bits suffice for over 80000 years.  */
Packit Service fdd496
              LONGLONG wait_until = counter_before.QuadPart + wait_ticks;
Packit Service fdd496
              /* Use Sleep for the longest part.  */
Packit Service fdd496
              if (sleep_millis > 0)
Packit Service fdd496
                Sleep (sleep_millis);
Packit Service fdd496
              /* Busy-loop for the rest.  */
Packit Service fdd496
              for (;;)
Packit Service fdd496
                {
Packit Service fdd496
                  LARGE_INTEGER counter_after;
Packit Service fdd496
                  if (!QueryPerformanceCounter (&counter_after))
Packit Service fdd496
                    /* QueryPerformanceCounter failed, but succeeded earlier.
Packit Service fdd496
                       Should not happen.  */
Packit Service fdd496
                    break;
Packit Service fdd496
                  if (counter_after.QuadPart >= wait_until)
Packit Service fdd496
                    /* The requested time has elapsed.  */
Packit Service fdd496
                    break;
Packit Service fdd496
                }
Packit Service fdd496
              goto done;
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
  /* Implementation for long delays and as fallback.  */
Packit Service fdd496
  Sleep (requested_delay->tv_sec * 1000 + requested_delay->tv_nsec / 1000000);
Packit Service fdd496
Packit Service fdd496
 done:
Packit Service fdd496
  /* Sleep is not interruptible.  So there is no remaining delay.  */
Packit Service fdd496
  if (remaining_delay != NULL)
Packit Service fdd496
    {
Packit Service fdd496
      remaining_delay->tv_sec = 0;
Packit Service fdd496
      remaining_delay->tv_nsec = 0;
Packit Service fdd496
    }
Packit Service fdd496
  return 0;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#else
Packit Service fdd496
/* Unix platforms lacking nanosleep. */
Packit Service fdd496
Packit Service fdd496
/* Some systems (MSDOS) don't have SIGCONT.
Packit Service fdd496
   Using SIGTERM here turns the signal-handling code below
Packit Service fdd496
   into a no-op on such systems. */
Packit Service fdd496
# ifndef SIGCONT
Packit Service fdd496
#  define SIGCONT SIGTERM
Packit Service fdd496
# endif
Packit Service fdd496
Packit Service fdd496
static sig_atomic_t volatile suspended;
Packit Service fdd496
Packit Service fdd496
/* Handle SIGCONT. */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
sighandler (int sig)
Packit Service fdd496
{
Packit Service fdd496
  suspended = 1;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Suspend execution for at least *TS_DELAY seconds.  */
Packit Service fdd496
Packit Service fdd496
static int
Packit Service fdd496
my_usleep (const struct timespec *ts_delay)
Packit Service fdd496
{
Packit Service fdd496
  struct timeval tv_delay;
Packit Service fdd496
  tv_delay.tv_sec = ts_delay->tv_sec;
Packit Service fdd496
  tv_delay.tv_usec = (ts_delay->tv_nsec + 999) / 1000;
Packit Service fdd496
  if (tv_delay.tv_usec == 1000000)
Packit Service fdd496
    {
Packit Service fdd496
      if (tv_delay.tv_sec == TYPE_MAXIMUM (time_t))
Packit Service fdd496
        tv_delay.tv_usec = 1000000 - 1; /* close enough */
Packit Service fdd496
      else
Packit Service fdd496
        {
Packit Service fdd496
          tv_delay.tv_sec++;
Packit Service fdd496
          tv_delay.tv_usec = 0;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
  return select (0, NULL, NULL, NULL, &tv_delay);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Suspend execution for at least *REQUESTED_DELAY seconds.  The
Packit Service fdd496
   *REMAINING_DELAY part isn't implemented yet.  */
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
nanosleep (const struct timespec *requested_delay,
Packit Service fdd496
           struct timespec *remaining_delay)
Packit Service fdd496
{
Packit Service fdd496
  static bool initialized;
Packit Service fdd496
Packit Service fdd496
  if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
Packit Service fdd496
    {
Packit Service fdd496
      errno = EINVAL;
Packit Service fdd496
      return -1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* set up sig handler */
Packit Service fdd496
  if (! initialized)
Packit Service fdd496
    {
Packit Service fdd496
      struct sigaction oldact;
Packit Service fdd496
Packit Service fdd496
      sigaction (SIGCONT, NULL, &oldact);
Packit Service fdd496
      if (get_handler (&oldact) != SIG_IGN)
Packit Service fdd496
        {
Packit Service fdd496
          struct sigaction newact;
Packit Service fdd496
Packit Service fdd496
          newact.sa_handler = sighandler;
Packit Service fdd496
          sigemptyset (&newact.sa_mask);
Packit Service fdd496
          newact.sa_flags = 0;
Packit Service fdd496
          sigaction (SIGCONT, &newact, NULL);
Packit Service fdd496
        }
Packit Service fdd496
      initialized = true;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  suspended = 0;
Packit Service fdd496
Packit Service fdd496
  if (my_usleep (requested_delay) == -1)
Packit Service fdd496
    {
Packit Service fdd496
      if (suspended)
Packit Service fdd496
        {
Packit Service fdd496
          /* Calculate time remaining.  */
Packit Service fdd496
          /* FIXME: the code in sleep doesn't use this, so there's no
Packit Service fdd496
             rush to implement it.  */
Packit Service fdd496
Packit Service fdd496
          errno = EINTR;
Packit Service fdd496
        }
Packit Service fdd496
      return -1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* FIXME: Restore sig handler?  */
Packit Service fdd496
Packit Service fdd496
  return 0;
Packit Service fdd496
}
Packit Service fdd496
#endif