Blame gnulib-tests/nap.h

Packit Service fdd496
/* Assist in file system timestamp tests.
Packit Service fdd496
   Copyright (C) 2009-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 Eric Blake <ebb9@byu.net>, 2009.  */
Packit Service fdd496
Packit Service fdd496
#ifndef GLTEST_NAP_H
Packit Service fdd496
# define GLTEST_NAP_H
Packit Service fdd496
Packit Service fdd496
# include <limits.h>
Packit Service fdd496
# include <stdbool.h>
Packit Service fdd496
Packit Service fdd496
/* Name of the witness file.  */
Packit Service fdd496
#define TEMPFILE BASE "nap.tmp"
Packit Service fdd496
Packit Service fdd496
/* File descriptor used for the witness file.  */
Packit Service fdd496
static int nap_fd = -1;
Packit Service fdd496
Packit Service fdd496
/* Return A - B, in ns.
Packit Service fdd496
   Return 0 if the true result would be negative.
Packit Service fdd496
   Return INT_MAX if the true result would be greater than INT_MAX.  */
Packit Service fdd496
static int
Packit Service fdd496
diff_timespec (struct timespec a, struct timespec b)
Packit Service fdd496
{
Packit Service fdd496
  time_t as = a.tv_sec;
Packit Service fdd496
  time_t bs = b.tv_sec;
Packit Service fdd496
  int ans = a.tv_nsec;
Packit Service fdd496
  int bns = b.tv_nsec;
Packit Service fdd496
Packit Service fdd496
  if (! (bs < as || (bs == as && bns < ans)))
Packit Service fdd496
    return 0;
Packit Service fdd496
  if (as - bs <= INT_MAX / 1000000000)
Packit Service fdd496
    {
Packit Service fdd496
      int sdiff = (as - bs) * 1000000000;
Packit Service fdd496
      int usdiff = ans - bns;
Packit Service fdd496
      if (usdiff < INT_MAX - sdiff)
Packit Service fdd496
        return sdiff + usdiff;
Packit Service fdd496
    }
Packit Service fdd496
  return INT_MAX;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* If DO_WRITE, bump the modification time of the file designated by NAP_FD.
Packit Service fdd496
   Then fetch the new STAT information of NAP_FD.  */
Packit Service fdd496
static void
Packit Service fdd496
nap_get_stat (struct stat *st, int do_write)
Packit Service fdd496
{
Packit Service fdd496
  if (do_write)
Packit Service fdd496
    {
Packit Service fdd496
      ASSERT (write (nap_fd, "\n", 1) == 1);
Packit Service fdd496
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit Service fdd496
      /* On native Windows, the modification times are not changed until NAP_FD
Packit Service fdd496
         is closed. See
Packit Service fdd496
         https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx */
Packit Service fdd496
      close (nap_fd);
Packit Service fdd496
      nap_fd = open (TEMPFILE, O_RDWR, 0600);
Packit Service fdd496
      ASSERT (nap_fd != -1);
Packit Service fdd496
      lseek (nap_fd, 0, SEEK_END);
Packit Service fdd496
#endif
Packit Service fdd496
    }
Packit Service fdd496
  ASSERT (fstat (nap_fd, st) == 0);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Given a file whose descriptor is FD, see whether delaying by DELAY
Packit Service fdd496
   nanoseconds causes a change in a file's mtime.
Packit Service fdd496
   OLD_ST is the file's status, recently gotten.  */
Packit Service fdd496
static bool
Packit Service fdd496
nap_works (int delay, struct stat old_st)
Packit Service fdd496
{
Packit Service fdd496
  struct stat st;
Packit Service fdd496
  struct timespec delay_spec;
Packit Service fdd496
  delay_spec.tv_sec = delay / 1000000000;
Packit Service fdd496
  delay_spec.tv_nsec = delay % 1000000000;
Packit Service fdd496
  ASSERT (nanosleep (&delay_spec, 0) == 0);
Packit Service fdd496
  nap_get_stat (&st, 1);
Packit Service fdd496
Packit Service fdd496
  if (diff_timespec (get_stat_mtime (&st), get_stat_mtime (&old_st)))
Packit Service fdd496
    return true;
Packit Service fdd496
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
clear_temp_file (void)
Packit Service fdd496
{
Packit Service fdd496
  if (0 <= nap_fd)
Packit Service fdd496
    {
Packit Service fdd496
      ASSERT (close (nap_fd) != -1);
Packit Service fdd496
      ASSERT (unlink (TEMPFILE) != -1);
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Sleep long enough to notice a timestamp difference on the file
Packit Service fdd496
   system in the current directory.  Use an adaptive approach, trying
Packit Service fdd496
   to find the smallest delay which works on the current file system
Packit Service fdd496
   to make the timestamp difference appear.  Assert a maximum delay of
Packit Service fdd496
   ~2 seconds, more precisely sum(2^n) from 0 to 30 = 2^31 - 1 = 2.1s.
Packit Service fdd496
   Assumes that BASE is defined, and requires that the test module
Packit Service fdd496
   depends on nanosleep.  */
Packit Service fdd496
static void
Packit Service fdd496
nap (void)
Packit Service fdd496
{
Packit Service fdd496
  struct stat old_st;
Packit Service fdd496
  static int delay = 1;
Packit Service fdd496
Packit Service fdd496
  if (-1 == nap_fd)
Packit Service fdd496
    {
Packit Service fdd496
      atexit (clear_temp_file);
Packit Service fdd496
      ASSERT ((nap_fd = creat (TEMPFILE, 0600)) != -1);
Packit Service fdd496
      nap_get_stat (&old_st, 0);
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      ASSERT (0 <= nap_fd);
Packit Service fdd496
      nap_get_stat (&old_st, 1);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (1 < delay)
Packit Service fdd496
    delay = delay / 2;  /* Try half of the previous delay.  */
Packit Service fdd496
  ASSERT (0 < delay);
Packit Service fdd496
Packit Service fdd496
  for (;;)
Packit Service fdd496
    {
Packit Service fdd496
      if (nap_works (delay, old_st))
Packit Service fdd496
        return;
Packit Service fdd496
      if (delay <= (2147483647 - 1) / 2)
Packit Service fdd496
        {
Packit Service fdd496
          delay = delay * 2 + 1;
Packit Service fdd496
          continue;
Packit Service fdd496
        }
Packit Service fdd496
      else
Packit Service fdd496
        break;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Bummer: even the highest nap delay didn't work. */
Packit Service fdd496
  ASSERT (0);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#endif /* GLTEST_NAP_H */