Blame gl/tests/nanosleep.c

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