Blame gnulib-tests/nap.h

Packit 33f14e
/* Assist in file system timestamp tests.
Packit 33f14e
   Copyright (C) 2009-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
/* Written by Eric Blake <ebb9@byu.net>, 2009.  */
Packit 33f14e
Packit 33f14e
#ifndef GLTEST_NAP_H
Packit 33f14e
# define GLTEST_NAP_H
Packit 33f14e
Packit 33f14e
# include <limits.h>
Packit 33f14e
# include <stdbool.h>
Packit 33f14e
Packit 33f14e
/* Name of the witness file.  */
Packit 33f14e
#define TEMPFILE BASE "nap.tmp"
Packit 33f14e
Packit 33f14e
/* File descriptor used for the witness file.  */
Packit 33f14e
static int nap_fd = -1;
Packit 33f14e
Packit 33f14e
/* Return A - B, in ns.
Packit 33f14e
   Return 0 if the true result would be negative.
Packit 33f14e
   Return INT_MAX if the true result would be greater than INT_MAX.  */
Packit 33f14e
static int
Packit 33f14e
diff_timespec (struct timespec a, struct timespec b)
Packit 33f14e
{
Packit 33f14e
  time_t as = a.tv_sec;
Packit 33f14e
  time_t bs = b.tv_sec;
Packit 33f14e
  int ans = a.tv_nsec;
Packit 33f14e
  int bns = b.tv_nsec;
Packit 33f14e
Packit 33f14e
  if (! (bs < as || (bs == as && bns < ans)))
Packit 33f14e
    return 0;
Packit 33f14e
  if (as - bs <= INT_MAX / 1000000000)
Packit 33f14e
    {
Packit 33f14e
      int sdiff = (as - bs) * 1000000000;
Packit 33f14e
      int usdiff = ans - bns;
Packit 33f14e
      if (usdiff < INT_MAX - sdiff)
Packit 33f14e
        return sdiff + usdiff;
Packit 33f14e
    }
Packit 33f14e
  return INT_MAX;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* If DO_WRITE, bump the modification time of the file designated by NAP_FD.
Packit 33f14e
   Then fetch the new STAT information of NAP_FD.  */
Packit 33f14e
static void
Packit 33f14e
nap_get_stat (struct stat *st, int do_write)
Packit 33f14e
{
Packit 33f14e
  if (do_write)
Packit 33f14e
    {
Packit 33f14e
      ASSERT (write (nap_fd, "\n", 1) == 1);
Packit 33f14e
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 33f14e
      /* On native Windows, the modification times are not changed until NAP_FD
Packit 33f14e
         is closed. See
Packit 33f14e
         https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx */
Packit 33f14e
      close (nap_fd);
Packit 33f14e
      nap_fd = open (TEMPFILE, O_RDWR, 0600);
Packit 33f14e
      ASSERT (nap_fd != -1);
Packit 33f14e
      lseek (nap_fd, 0, SEEK_END);
Packit 33f14e
#endif
Packit 33f14e
    }
Packit 33f14e
  ASSERT (fstat (nap_fd, st) == 0);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Given a file whose descriptor is FD, see whether delaying by DELAY
Packit 33f14e
   nanoseconds causes a change in a file's mtime.
Packit 33f14e
   OLD_ST is the file's status, recently gotten.  */
Packit 33f14e
static bool
Packit 33f14e
nap_works (int delay, struct stat old_st)
Packit 33f14e
{
Packit 33f14e
  struct stat st;
Packit 33f14e
  struct timespec delay_spec;
Packit 33f14e
  delay_spec.tv_sec = delay / 1000000000;
Packit 33f14e
  delay_spec.tv_nsec = delay % 1000000000;
Packit 33f14e
  ASSERT (nanosleep (&delay_spec, 0) == 0);
Packit 33f14e
  nap_get_stat (&st, 1);
Packit 33f14e
Packit 33f14e
  if (diff_timespec (get_stat_mtime (&st), get_stat_mtime (&old_st)))
Packit 33f14e
    return true;
Packit 33f14e
Packit 33f14e
  return false;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
clear_temp_file (void)
Packit 33f14e
{
Packit 33f14e
  if (0 <= nap_fd)
Packit 33f14e
    {
Packit 33f14e
      ASSERT (close (nap_fd) != -1);
Packit 33f14e
      ASSERT (unlink (TEMPFILE) != -1);
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Sleep long enough to notice a timestamp difference on the file
Packit 33f14e
   system in the current directory.  Use an adaptive approach, trying
Packit 33f14e
   to find the smallest delay which works on the current file system
Packit 33f14e
   to make the timestamp difference appear.  Assert a maximum delay of
Packit 33f14e
   ~2 seconds, more precisely sum(2^n) from 0 to 30 = 2^31 - 1 = 2.1s.
Packit 33f14e
   Assumes that BASE is defined, and requires that the test module
Packit 33f14e
   depends on nanosleep.  */
Packit 33f14e
static void
Packit 33f14e
nap (void)
Packit 33f14e
{
Packit 33f14e
  struct stat old_st;
Packit 33f14e
  static int delay = 1;
Packit 33f14e
Packit 33f14e
  if (-1 == nap_fd)
Packit 33f14e
    {
Packit 33f14e
      atexit (clear_temp_file);
Packit 33f14e
      ASSERT ((nap_fd = creat (TEMPFILE, 0600)) != -1);
Packit 33f14e
      nap_get_stat (&old_st, 0);
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      ASSERT (0 <= nap_fd);
Packit 33f14e
      nap_get_stat (&old_st, 1);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (1 < delay)
Packit 33f14e
    delay = delay / 2;  /* Try half of the previous delay.  */
Packit 33f14e
  ASSERT (0 < delay);
Packit 33f14e
Packit 33f14e
  for (;;)
Packit 33f14e
    {
Packit 33f14e
      if (nap_works (delay, old_st))
Packit 33f14e
        return;
Packit 33f14e
      if (delay <= (2147483647 - 1) / 2)
Packit 33f14e
        {
Packit 33f14e
          delay = delay * 2 + 1;
Packit 33f14e
          continue;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        break;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* Bummer: even the highest nap delay didn't work. */
Packit 33f14e
  ASSERT (0);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#endif /* GLTEST_NAP_H */