(root)/
sed-4.9/
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  
      24  # include <stdckdint.h>
      25  
      26  /* Avoid a conflict with a function called nap() on UnixWare.  */
      27  # if defined _SCO_DS || (defined __SCO_VERSION__ || defined __sysv5__)  /* OpenServer, UnixWare */
      28  #  include <unistd.h>
      29  #  undef nap
      30  #  define nap gl_nap
      31  # endif
      32  
      33  /* Name of the witness file.  */
      34  #define TEMPFILE BASE "nap.tmp"
      35  
      36  /* File descriptor used for the witness file.  */
      37  static int nap_fd = -1;
      38  
      39  /* Return A - B, in ns.
      40     Return 0 if the true result would be negative.
      41     Return INT_MAX if the true result would be greater than INT_MAX.  */
      42  static int
      43  diff_timespec (struct timespec a, struct timespec b)
      44  {
      45    time_t as = a.tv_sec;
      46    time_t bs = b.tv_sec;
      47    int ans = a.tv_nsec;
      48    int bns = b.tv_nsec;
      49    int sdiff;
      50  
      51    ASSERT (0 <= ans && ans < 2000000000);
      52    ASSERT (0 <= bns && bns < 2000000000);
      53  
      54    if (! (bs < as || (bs == as && bns < ans)))
      55      return 0;
      56  
      57    if (ckd_sub (&sdiff, as, bs)
      58        || ckd_mul (&sdiff, sdiff, 1000000000)
      59        || ckd_add (&sdiff, sdiff, ans - bns))
      60      return INT_MAX;
      61  
      62    return sdiff;
      63  }
      64  
      65  /* If DO_WRITE, bump the modification time of the file designated by NAP_FD.
      66     Then fetch the new STAT information of NAP_FD.  */
      67  static void
      68  nap_get_stat (struct stat *st, int do_write)
      69  {
      70    if (do_write)
      71      {
      72        ASSERT (write (nap_fd, "\n", 1) == 1);
      73  #if defined _WIN32 || defined __CYGWIN__
      74        /* On Windows, the modification times are not changed until NAP_FD
      75           is closed. See
      76           <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-writefile> */
      77        close (nap_fd);
      78        nap_fd = open (TEMPFILE, O_RDWR, 0600);
      79        ASSERT (nap_fd != -1);
      80        lseek (nap_fd, 0, SEEK_END);
      81  #endif
      82      }
      83    ASSERT (fstat (nap_fd, st) == 0);
      84  }
      85  
      86  /* Given a file whose descriptor is FD, see whether delaying by DELAY
      87     nanoseconds causes a change in a file's mtime.
      88     OLD_ST is the file's status, recently gotten.  */
      89  static bool
      90  nap_works (int delay, struct stat old_st)
      91  {
      92    struct stat st;
      93    struct timespec delay_spec;
      94    delay_spec.tv_sec = delay / 1000000000;
      95    delay_spec.tv_nsec = delay % 1000000000;
      96    ASSERT (nanosleep (&delay_spec, 0) == 0);
      97    nap_get_stat (&st, 1);
      98  
      99    if (diff_timespec (get_stat_mtime (&st), get_stat_mtime (&old_st)))
     100      return true;
     101  
     102    return false;
     103  }
     104  
     105  static void
     106  clear_temp_file (void)
     107  {
     108    if (0 <= nap_fd)
     109      {
     110        ASSERT (close (nap_fd) != -1);
     111        ASSERT (unlink (TEMPFILE) != -1);
     112      }
     113  }
     114  
     115  /* Sleep long enough to notice a timestamp difference on the file
     116     system in the current directory.  Use an adaptive approach, trying
     117     to find the smallest delay which works on the current file system
     118     to make the timestamp difference appear.  Assert a maximum delay of
     119     ~2 seconds, more precisely sum(2^n) from 0 to 30 = 2^31 - 1 = 2.1s.
     120     Assumes that BASE is defined, and requires that the test module
     121     depends on nanosleep.  */
     122  static void
     123  nap (void)
     124  {
     125    struct stat old_st;
     126    static int delay = 1;
     127  
     128    if (-1 == nap_fd)
     129      {
     130        atexit (clear_temp_file);
     131        ASSERT ((nap_fd = creat (TEMPFILE, 0600)) != -1);
     132        nap_get_stat (&old_st, 0);
     133      }
     134    else
     135      {
     136        ASSERT (0 <= nap_fd);
     137        nap_get_stat (&old_st, 1);
     138      }
     139  
     140    if (1 < delay)
     141      delay = delay / 2;  /* Try half of the previous delay.  */
     142    ASSERT (0 < delay);
     143  
     144    for (;;)
     145      {
     146        if (nap_works (delay, old_st))
     147          return;
     148        if (delay <= (2147483647 - 1) / 2)
     149          {
     150            delay = delay * 2 + 1;
     151            continue;
     152          }
     153        else
     154          break;
     155      }
     156  
     157    /* Bummer: even the highest nap delay didn't work. */
     158    ASSERT (0);
     159  }
     160  
     161  #endif /* GLTEST_NAP_H */