/* Provide a replacement for the POSIX nanosleep function.

   Copyright (C) 1999-2000, 2002, 2004-2026 Free Software Foundation, Inc.

   This file is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.

   This file 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 Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

/* written by Jim Meyering
   and Bruno Haible for the native Windows part */

#include <config.h>

#include <time.h>

#include "intprops.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/select.h>
#include <signal.h>

#include <errno.h>

#include <unistd.h>


enum { BILLION = 1000 * 1000 * 1000 };

#if HAVE_BUG_BIG_NANOSLEEP

int
nanosleep (const struct timespec *requested_delay,
           struct timespec *remaining_delay)
# undef nanosleep
{
  /* nanosleep mishandles large sleeps due to internal overflow problems.
     The worst known case of this is Linux 2.6.9 with glibc 2.3.4, which
     can't sleep more than 24.85 days (2^31 milliseconds).  Similarly,
     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.  */

  if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
    {
      errno = EINVAL;
      return -1;
    }

  {
    /* Verify that time_t is large enough.  */
    static_assert (TYPE_MAXIMUM (time_t) / 24 / 24 / 60 / 60);
    const time_t limit = 24 * 24 * 60 * 60;
    time_t seconds = requested_delay->tv_sec;
    struct timespec intermediate = *requested_delay;

    while (limit < seconds)
      {
        intermediate.tv_sec = limit;
        int result = nanosleep (&intermediate, remaining_delay);
        seconds -= limit;
        if (result)
          {
            if (remaining_delay)
              remaining_delay->tv_sec += seconds;
            return result;
          }
        intermediate.tv_nsec = 0;
      }
    intermediate.tv_sec = seconds;
    return nanosleep (&intermediate, remaining_delay);
  }
}

#elif defined _WIN32 && ! defined __CYGWIN__
/* Native Windows platforms.  */

# define WIN32_LEAN_AND_MEAN
# include <windows.h>

/* The Windows API function Sleep() has a resolution of about 15 ms and takes
   at least 5 ms to execute.  We use this function for longer time periods.
   Additionally, we use busy-looping over short time periods, to get a
   resolution of about 0.01 ms.  In order to measure such short timespans,
   we use the QueryPerformanceCounter() function.  */

int
nanosleep (const struct timespec *requested_delay,
           struct timespec *remaining_delay)
{
  static bool initialized;
  /* Number of performance counter increments per nanosecond,
     or zero if it could not be determined.  */
  static double ticks_per_nanosecond;

  if (requested_delay->tv_nsec < 0 || BILLION <= requested_delay->tv_nsec)
    {
      errno = EINVAL;
      return -1;
    }

  /* For requested delays of one second or more, 15ms resolution is
     sufficient.  */
  if (requested_delay->tv_sec == 0)
    {
      if (!initialized)
        {
          /* Initialize ticks_per_nanosecond.  */
          LARGE_INTEGER ticks_per_second;

          if (QueryPerformanceFrequency (&ticks_per_second))
            ticks_per_nanosecond =
              (double) ticks_per_second.QuadPart / 1000000000.0;

          initialized = true;
        }
      if (ticks_per_nanosecond)
        {
          /* QueryPerformanceFrequency worked.  We can use
             QueryPerformanceCounter.  Use a combination of Sleep and
             busy-looping.  */
          /* Number of milliseconds to pass to the Sleep function.
             Since Sleep can take up to 8 ms less or 8 ms more than requested
             (or maybe more if the system is loaded), we subtract 10 ms.  */
          int sleep_millis = (int) requested_delay->tv_nsec / 1000000 - 10;
          /* Determine how many ticks to delay.  */
          LONGLONG wait_ticks = requested_delay->tv_nsec * ticks_per_nanosecond;
          /* Start.  */
          LARGE_INTEGER counter_before;
          if (QueryPerformanceCounter (&counter_before))
            {
              /* Wait until the performance counter has reached this value.
                 We don't need to worry about overflow, because the performance
                 counter is reset at reboot, and with a frequency of 3.6E6
                 ticks per second 63 bits suffice for over 80000 years.  */
              LONGLONG wait_until = counter_before.QuadPart + wait_ticks;
              /* Use Sleep for the longest part.  */
              if (sleep_millis > 0)
                Sleep (sleep_millis);
              /* Busy-loop for the rest.  */
              for (;;)
                {
                  LARGE_INTEGER counter_after;
                  if (!QueryPerformanceCounter (&counter_after))
                    /* QueryPerformanceCounter failed, but succeeded earlier.
                       Should not happen.  */
                    break;
                  if (counter_after.QuadPart >= wait_until)
                    /* The requested time has elapsed.  */
                    break;
                }
              goto done;
            }
        }
    }
  /* Implementation for long delays and as fallback.  */
  Sleep (requested_delay->tv_sec * 1000 + requested_delay->tv_nsec / 1000000);

 done:
  /* Sleep is not interruptible.  So there is no remaining delay.  */
  if (remaining_delay != NULL)
    {
      remaining_delay->tv_sec = 0;
      remaining_delay->tv_nsec = 0;
    }
  return 0;
}

#else
/* Other platforms lacking nanosleep.
   It's not clear whether these are still practical porting targets.
   For now, just fall back on pselect.  */

/* 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)
{
  return pselect (0, NULL, NULL, NULL, requested_delay, NULL);
}
#endif
