d8dbad2975555b219b60870d803c43a1e7e457e1
[gnulib.git] / tests / nap.h
1 /* Assist in file system timestamp tests.
2    Copyright (C) 2009-2013 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 <http://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 /* Return A - B, in ns.
25    Return 0 if the true result would be negative.
26    Return INT_MAX if the true result would be greater than INT_MAX.  */
27 static int
28 diff_timespec (struct timespec a, struct timespec b)
29 {
30   time_t as = a.tv_sec;
31   time_t bs = b.tv_sec;
32   int ans = a.tv_nsec;
33   int bns = b.tv_nsec;
34
35   if (! (bs < as || (bs == as && bns < ans)))
36     return 0;
37   if (as - bs <= INT_MAX / 1000000000)
38     {
39       int sdiff = (as - bs) * 1000000000;
40       int usdiff = ans - bns;
41       if (usdiff < INT_MAX - sdiff)
42         return sdiff + usdiff;
43     }
44   return INT_MAX;
45 }
46
47 static void
48 get_stat (int fd, struct stat *st, int do_write)
49 {
50   if (do_write)
51     ASSERT (write (fd, "\n", 1) == 1);
52   ASSERT (fstat (fd, st) == 0);
53 }
54
55 /* Given a file whose descriptor is FD, see whether delaying by DELAY
56    nanoseconds causes a change in a file's time stamp.  *ST is the
57    file's status, recently gotten.  Update *ST to reflect the latest
58    status gotten.  If successful, return the needed delay, in
59    nanoseconds as determined by the observed time stamps; this may be
60    greater than DELAY if we crossed a quantization boundary.  If
61    unsuccessful, return 0.  */
62 static int
63 nap_works (int fd, int delay, struct stat *st)
64 {
65   struct stat old_st = *st;
66   struct timespec delay_spec;
67   int cdiff, mdiff;
68   delay_spec.tv_sec = delay / 1000000000;
69   delay_spec.tv_nsec = delay % 1000000000;
70   ASSERT (nanosleep (&delay_spec, 0) == 0);
71   get_stat (fd, st, 1);
72
73   /* Return the greater of the ctime and the mtime differences, or
74      zero if it cannot be determined, or INT_MAX if either overflows.  */
75   cdiff = diff_timespec (get_stat_ctime (st), get_stat_ctime (&old_st));
76   if (cdiff != 0)
77     {
78       mdiff = diff_timespec (get_stat_mtime (st), get_stat_mtime (&old_st));
79       if (mdiff != 0)
80         return cdiff < mdiff ? mdiff : cdiff;
81     }
82   return 0;
83 }
84
85 static int
86 guess_delay (void)
87 {
88   /* Try a 1-ns sleep first, for speed.  If that doesn't work, try 100
89      ns, 1 microsecond, 1 ms, etc.  xfs has a quantization of about 10
90      milliseconds, even though it has a granularity of 1 nanosecond,
91      and NTFS has a default quantization of 15.25 milliseconds, even
92      though it has a granularity of 100 nanoseconds, so 15.25 ms is a
93      good quantization to try.  If that doesn't work, try 1 second.
94      The worst case is 2 seconds, needed for FAT.  */
95   static int const delaytab[] = {1, 1000, 1000000, 15250000, 1000000000 };
96   int fd = creat (BASE "tmp", 0600);
97   int i;
98   int delay = 2000000000;
99   struct stat st;
100   ASSERT (0 <= fd);
101   get_stat (fd, &st, 0);
102   for (i = 0; i < sizeof delaytab / sizeof delaytab[0]; i++)
103     {
104       int d = nap_works (fd, delaytab[i], &st);
105       if (d != 0)
106         {
107           delay = d;
108           break;
109         }
110     }
111   ASSERT (close (fd) == 0);
112   ASSERT (unlink (BASE "tmp") == 0);
113   return delay;
114 }
115
116 /* Sleep long enough to notice a timestamp difference on the file
117    system in the current directory.  Assumes that BASE is defined,
118    and requires that the test module depends on nanosleep.  */
119 static void
120 nap (void)
121 {
122   static struct timespec delay;
123   if (!delay.tv_sec && !delay.tv_nsec)
124     {
125       int d = guess_delay ();
126
127       /* Multiply by 1.125 (rounding up), to avoid problems if the
128          file system's clock is a bit slower than nanosleep's.
129          Ceiling it at INT_MAX, though.  */
130       int delta = (d >> 3) + ((d & 7) != 0);
131       d = delta < INT_MAX - d ? d + delta : INT_MAX;
132       delay.tv_sec = d / 1000000000;
133       delay.tv_nsec = d % 1000000000;
134     }
135   ASSERT (nanosleep (&delay, 0) == 0);
136 }
137
138 #endif /* GLTEST_NAP_H */