(root)/
findutils-4.9.0/
gnulib-tests/
nap.h
       1  /* Assist in file system timestamp tests.
       2     Copyright (C) 2009-2022 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Eric Blake <ebb9@byu.net>, 2009.  */
      18  
      19  #ifndef GLTEST_NAP_H
      20  # define GLTEST_NAP_H
      21  
      22  # include <limits.h>
      23  # include <stdbool.h>
      24  
      25  # include <intprops.h>
      26  
      27  /* Avoid a conflict with a function called nap() on UnixWare.  */
      28  # if defined _SCO_DS || (defined __SCO_VERSION__ || defined __sysv5__)  /* OpenServer, UnixWare */
      29  #  include <unistd.h>
      30  #  undef nap
      31  #  define nap gl_nap
      32  # endif
      33  
      34  /* Name of the witness file.  */
      35  #define TEMPFILE BASE "nap.tmp"
      36  
      37  /* File descriptor used for the witness file.  */
      38  static int nap_fd = -1;
      39  
      40  /* Return A - B, in ns.
      41     Return 0 if the true result would be negative.
      42     Return INT_MAX if the true result would be greater than INT_MAX.  */
      43  static int
      44  diff_timespec (struct timespec a, struct timespec b)
      45  {
      46    time_t as = a.tv_sec;
      47    time_t bs = b.tv_sec;
      48    int ans = a.tv_nsec;
      49    int bns = b.tv_nsec;
      50    int sdiff;
      51  
      52    ASSERT (0 <= ans && ans < 2000000000);
      53    ASSERT (0 <= bns && bns < 2000000000);
      54  
      55    if (! (bs < as || (bs == as && bns < ans)))
      56      return 0;
      57  
      58    if (INT_SUBTRACT_WRAPV (as, bs, &sdiff)
      59        || INT_MULTIPLY_WRAPV (sdiff, 1000000000, &sdiff)
      60        || INT_ADD_WRAPV (sdiff, ans - bns, &sdiff))
      61      return INT_MAX;
      62  
      63    return sdiff;
      64  }
      65  
      66  /* If DO_WRITE, bump the modification time of the file designated by NAP_FD.
      67     Then fetch the new STAT information of NAP_FD.  */
      68  static void
      69  nap_get_stat (struct stat *st, int do_write)
      70  {
      71    if (do_write)
      72      {
      73        ASSERT (write (nap_fd, "\n", 1) == 1);
      74  #if defined _WIN32 || defined __CYGWIN__
      75        /* On Windows, the modification times are not changed until NAP_FD
      76           is closed. See
      77           <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-writefile> */
      78        close (nap_fd);
      79        nap_fd = open (TEMPFILE, O_RDWR, 0600);
      80        ASSERT (nap_fd != -1);
      81        lseek (nap_fd, 0, SEEK_END);
      82  #endif
      83      }
      84    ASSERT (fstat (nap_fd, st) == 0);
      85  }
      86  
      87  /* Given a file whose descriptor is FD, see whether delaying by DELAY
      88     nanoseconds causes a change in a file's mtime.
      89     OLD_ST is the file's status, recently gotten.  */
      90  static bool
      91  nap_works (int delay, struct stat old_st)
      92  {
      93    struct stat st;
      94    struct timespec delay_spec;
      95    delay_spec.tv_sec = delay / 1000000000;
      96    delay_spec.tv_nsec = delay % 1000000000;
      97    ASSERT (nanosleep (&delay_spec, 0) == 0);
      98    nap_get_stat (&st, 1);
      99  
     100    if (diff_timespec (get_stat_mtime (&st), get_stat_mtime (&old_st)))
     101      return true;
     102  
     103    return false;
     104  }
     105  
     106  static void
     107  clear_temp_file (void)
     108  {
     109    if (0 <= nap_fd)
     110      {
     111        ASSERT (close (nap_fd) != -1);
     112        ASSERT (unlink (TEMPFILE) != -1);
     113      }
     114  }
     115  
     116  /* Sleep long enough to notice a timestamp difference on the file
     117     system in the current directory.  Use an adaptive approach, trying
     118     to find the smallest delay which works on the current file system
     119     to make the timestamp difference appear.  Assert a maximum delay of
     120     ~2 seconds, more precisely sum(2^n) from 0 to 30 = 2^31 - 1 = 2.1s.
     121     Assumes that BASE is defined, and requires that the test module
     122     depends on nanosleep.  */
     123  static void
     124  nap (void)
     125  {
     126    struct stat old_st;
     127    static int delay = 1;
     128  
     129    if (-1 == nap_fd)
     130      {
     131        atexit (clear_temp_file);
     132        ASSERT ((nap_fd = creat (TEMPFILE, 0600)) != -1);
     133        nap_get_stat (&old_st, 0);
     134      }
     135    else
     136      {
     137        ASSERT (0 <= nap_fd);
     138        nap_get_stat (&old_st, 1);
     139      }
     140  
     141    if (1 < delay)
     142      delay = delay / 2;  /* Try half of the previous delay.  */
     143    ASSERT (0 < delay);
     144  
     145    for (;;)
     146      {
     147        if (nap_works (delay, old_st))
     148          return;
     149        if (delay <= (2147483647 - 1) / 2)
     150          {
     151            delay = delay * 2 + 1;
     152            continue;
     153          }
     154        else
     155          break;
     156      }
     157  
     158    /* Bummer: even the highest nap delay didn't work. */
     159    ASSERT (0);
     160  }
     161  
     162  #endif /* GLTEST_NAP_H */