maint: update copyright
[gnulib.git] / tests / nap.h
1 /* Assist in file system timestamp tests.
2    Copyright (C) 2009-2014 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 # include <stdbool.h>
24
25 /* File descriptor used for the witness file.  */
26 static int nap_fd = -1;
27
28 /* Return A - B, in ns.
29    Return 0 if the true result would be negative.
30    Return INT_MAX if the true result would be greater than INT_MAX.  */
31 static int
32 diff_timespec (struct timespec a, struct timespec b)
33 {
34   time_t as = a.tv_sec;
35   time_t bs = b.tv_sec;
36   int ans = a.tv_nsec;
37   int bns = b.tv_nsec;
38
39   if (! (bs < as || (bs == as && bns < ans)))
40     return 0;
41   if (as - bs <= INT_MAX / 1000000000)
42     {
43       int sdiff = (as - bs) * 1000000000;
44       int usdiff = ans - bns;
45       if (usdiff < INT_MAX - sdiff)
46         return sdiff + usdiff;
47     }
48   return INT_MAX;
49 }
50
51 static void
52 get_stat (int fd, struct stat *st, int do_write)
53 {
54   if (do_write)
55     ASSERT (write (fd, "\n", 1) == 1);
56   ASSERT (fstat (fd, st) == 0);
57 }
58
59 /* Given a file whose descriptor is FD, see whether delaying by DELAY
60    nanoseconds causes a change in a file's ctime and mtime.
61    OLD_ST is the file's status, recently gotten.  */
62 static bool
63 nap_works (int fd, int delay, struct stat old_st)
64 {
65   struct stat st;
66   struct timespec delay_spec;
67   delay_spec.tv_sec = delay / 1000000000;
68   delay_spec.tv_nsec = delay % 1000000000;
69   ASSERT (nanosleep (&delay_spec, 0) == 0);
70   get_stat (fd, &st, 1);
71
72   if (   diff_timespec (get_stat_ctime (&st), get_stat_ctime (&old_st))
73       && diff_timespec (get_stat_mtime (&st), get_stat_mtime (&old_st)))
74     return true;
75
76   return false;
77 }
78
79 #define TEMPFILE BASE "nap.tmp"
80
81 static void
82 clear_temp_file (void)
83 {
84   if (0 <= nap_fd)
85     {
86       ASSERT (close (nap_fd) != -1);
87       ASSERT (unlink (TEMPFILE) != -1);
88     }
89 }
90
91 /* Sleep long enough to notice a timestamp difference on the file
92    system in the current directory.  Use an adaptive approach, trying
93    to find the smallest delay which works on the current file system
94    to make the timestamp difference appear.  Assert a maximum delay of
95    ~2 seconds, more precisely sum(2^n) from 0 to 30 = 2^31 - 1 = 2.1s.
96    Assumes that BASE is defined, and requires that the test module
97    depends on nanosleep.  */
98 static void
99 nap (void)
100 {
101   struct stat old_st;
102   static int delay = 1;
103
104   if (-1 == nap_fd)
105     {
106       atexit (clear_temp_file);
107       ASSERT ((nap_fd = creat (TEMPFILE, 0600)) != -1);
108       get_stat (nap_fd, &old_st, 0);
109     }
110   else
111     {
112       ASSERT (0 <= nap_fd);
113       get_stat (nap_fd, &old_st, 1);
114     }
115
116   if (1 < delay)
117     delay = delay / 2;  /* Try half of the previous delay.  */
118   ASSERT (0 < delay);
119
120   for ( ; delay <= 2147483647; delay = delay * 2)
121     if (nap_works (nap_fd, delay, old_st))
122       return;
123
124   /* Bummer: even the highest nap delay didn't work. */
125   ASSERT (0);
126 }
127
128 #endif /* GLTEST_NAP_H */