1
0
mirror of https://git.savannah.gnu.org/git/gnulib.git synced 2025-08-20 11:01:20 +03:00

utimensat-tests, etc.: try to fix some races

Problem reported by Bernhard Voelker in
<http://lists.gnu.org/archive/html/bug-gnulib/2013-04/msg00071.html>.
I don't know whether this patch fixes that race condition, but it
fixes *some* race conditions, so it should be a win.
* modules/chown-tests (Depends-on):
* modules/fchownat-tests (Depends-on):
* modules/fdutimensat-tests (Depends-on):
* modules/futimens-tests (Depends-on):
* modules/lchown-tests (Depends-on):
* modules/stat-time-tests (Depends-on):
* modules/utimens-tests (Depends-on):
* modules/utimensat-tests (Depends-on):
Depend on nanosleep, not usleep.
* modules/chown-tests (test_chown_LDADD):
* modules/lchown-tests (test_lchown_LDADD):
* modules/stat-time-tests (test_stat_time_LDADD):
New macro.
* modules/fchownat-tests (test_fchownat_LDADD):
* modules/fdutimensat-tests (test_fdutimensat_LDADD):
* modules/futimens-tests (test_futimens_LDADD):
* modules/utimens-tests (test_utimens_LDADD):
* modules/utimensat-tests (test_utimensat_LDADD):
Add $(LIB_NANOSLEEP).
* modules/stat-time-tests (Files): Add tests/nap.h.
* tests/nap.h: Include <limits.h>, for INT_MAX.
(lt_mtime): Remove.
(diff_timespec): New function.
(get_stat): Rename from get_mtime.  All callers changed.
(nap_works): Determine the needed delay by inspecting the
file system's timestamp jumps; this should be more reliable.
Look at both mtime and ctime, and take the maximum of the two jumps.
(nap_works, guess_delay):
Return a nanosecond cound, not a microsecond count.
All callers changed.
(nap_works, nap): Use nanosleep, not usleep.  Check for nanosleep
failure.
(nap): Multiply the guess by 1.125, to accommodate the case where
the file system's clock is a bit slower than nanosleep's clock.
* tests/test-stat-time.c (BASE): New macro.
Include nap.h.
(nap): Remove; nap.h now defines this.  This removes a duplicate
implementation of 'nap'.
This commit is contained in:
Paul Eggert
2013-04-30 23:14:19 -07:00
parent 4bbfd28498
commit 5191133e01
11 changed files with 141 additions and 96 deletions

View File

@@ -1,5 +1,49 @@
2013-04-30 Paul Eggert <eggert@cs.ucla.edu> 2013-04-30 Paul Eggert <eggert@cs.ucla.edu>
utimensat-tests, etc.: try to fix some races
Problem reported by Bernhard Voelker in
<http://lists.gnu.org/archive/html/bug-gnulib/2013-04/msg00071.html>.
I don't know whether this patch fixes that race condition, but it
fixes *some* race conditions, so it should be a win.
* modules/chown-tests (Depends-on):
* modules/fchownat-tests (Depends-on):
* modules/fdutimensat-tests (Depends-on):
* modules/futimens-tests (Depends-on):
* modules/lchown-tests (Depends-on):
* modules/stat-time-tests (Depends-on):
* modules/utimens-tests (Depends-on):
* modules/utimensat-tests (Depends-on):
Depend on nanosleep, not usleep.
* modules/chown-tests (test_chown_LDADD):
* modules/lchown-tests (test_lchown_LDADD):
* modules/stat-time-tests (test_stat_time_LDADD):
New macro.
* modules/fchownat-tests (test_fchownat_LDADD):
* modules/fdutimensat-tests (test_fdutimensat_LDADD):
* modules/futimens-tests (test_futimens_LDADD):
* modules/utimens-tests (test_utimens_LDADD):
* modules/utimensat-tests (test_utimensat_LDADD):
Add $(LIB_NANOSLEEP).
* modules/stat-time-tests (Files): Add tests/nap.h.
* tests/nap.h: Include <limits.h>, for INT_MAX.
(lt_mtime): Remove.
(diff_timespec): New function.
(get_stat): Rename from get_mtime. All callers changed.
(nap_works): Determine the needed delay by inspecting the
file system's timestamp jumps; this should be more reliable.
Look at both mtime and ctime, and take the maximum of the two jumps.
(nap_works, guess_delay):
Return a nanosecond cound, not a microsecond count.
All callers changed.
(nap_works, nap): Use nanosleep, not usleep. Check for nanosleep
failure.
(nap): Multiply the guess by 1.125, to accommodate the case where
the file system's clock is a bit slower than nanosleep's clock.
* tests/test-stat-time.c (BASE): New macro.
Include nap.h.
(nap): Remove; nap.h now defines this. This removes a duplicate
implementation of 'nap'.
utimens, utimensat: work around Solaris UTIME_OMIT bug utimens, utimensat: work around Solaris UTIME_OMIT bug
Solaris 11.1 and Solaris 10 have the same UTIME_OMIT bug that Solaris 11.1 and Solaris 10 have the same UTIME_OMIT bug that
Linux kernel 2.6.32 does. Work around it in the same way. Linux kernel 2.6.32 does. Work around it in the same way.

View File

@@ -9,7 +9,7 @@ Depends-on:
ignore-value ignore-value
lstat lstat
mgetgroups mgetgroups
usleep nanosleep
stat-time stat-time
stdbool stdbool
symlink symlink
@@ -20,3 +20,4 @@ AC_CHECK_FUNCS_ONCE([getegid])
Makefile.am: Makefile.am:
TESTS += test-chown TESTS += test-chown
check_PROGRAMS += test-chown check_PROGRAMS += test-chown
test_chown_LDADD = $(LDADD) $(LIB_NANOSLEEP)

View File

@@ -9,9 +9,9 @@ tests/macros.h
Depends-on: Depends-on:
ignore-value ignore-value
mgetgroups mgetgroups
nanosleep
openat-h openat-h
progname progname
usleep
stat-time stat-time
symlink symlink
@@ -21,4 +21,4 @@ AC_CHECK_FUNCS_ONCE([getegid])
Makefile.am: Makefile.am:
TESTS += test-fchownat TESTS += test-fchownat
check_PROGRAMS += test-fchownat check_PROGRAMS += test-fchownat
test_fchownat_LDADD = $(LDADD) @LIBINTL@ test_fchownat_LDADD = $(LDADD) $(LIB_NANOSLEEP) @LIBINTL@

View File

@@ -10,10 +10,10 @@ tests/macros.h
Depends-on: Depends-on:
fcntl-h fcntl-h
ignore-value ignore-value
nanosleep
openat openat
timespec timespec
dup dup
usleep
utimecmp utimecmp
configure.ac: configure.ac:
@@ -21,4 +21,5 @@ configure.ac:
Makefile.am: Makefile.am:
TESTS += test-fdutimensat TESTS += test-fdutimensat
check_PROGRAMS += test-fdutimensat check_PROGRAMS += test-fdutimensat
test_fdutimensat_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBINTL@ test_fdutimensat_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) \
$(LIB_NANOSLEEP) @LIBINTL@

View File

@@ -10,9 +10,9 @@ Depends-on:
gettext gettext
fcntl-h fcntl-h
ignore-value ignore-value
nanosleep
timespec timespec
dup dup
usleep
utimecmp utimecmp
configure.ac: configure.ac:
@@ -20,4 +20,4 @@ configure.ac:
Makefile.am: Makefile.am:
TESTS += test-futimens TESTS += test-futimens
check_PROGRAMS += test-futimens check_PROGRAMS += test-futimens
test_futimens_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBINTL@ test_futimens_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_NANOSLEEP) @LIBINTL@

View File

@@ -8,7 +8,7 @@ tests/macros.h
Depends-on: Depends-on:
ignore-value ignore-value
mgetgroups mgetgroups
usleep nanosleep
stat-time stat-time
stdbool stdbool
symlink symlink
@@ -19,3 +19,4 @@ AC_CHECK_FUNCS_ONCE([getegid])
Makefile.am: Makefile.am:
TESTS += test-lchown TESTS += test-lchown
check_PROGRAMS += test-lchown check_PROGRAMS += test-lchown
test_lchown_LDADD = $(LDADD) $(LIB_NANOSLEEP)

View File

@@ -1,13 +1,15 @@
Files: Files:
tests/test-stat-time.c tests/test-stat-time.c
tests/macros.h tests/macros.h
tests/nap.h
Depends-on: Depends-on:
nanosleep
time time
usleep
configure.ac: configure.ac:
Makefile.am: Makefile.am:
TESTS += test-stat-time TESTS += test-stat-time
check_PROGRAMS += test-stat-time check_PROGRAMS += test-stat-time
test_stat_time_LDADD = $(LDADD) $(LIB_NANOSLEEP)

View File

@@ -11,9 +11,9 @@ Depends-on:
dup dup
gettext gettext
ignore-value ignore-value
nanosleep
symlink symlink
timespec timespec
usleep
utimecmp utimecmp
configure.ac: configure.ac:
@@ -21,4 +21,4 @@ configure.ac:
Makefile.am: Makefile.am:
TESTS += test-utimens TESTS += test-utimens
check_PROGRAMS += test-utimens check_PROGRAMS += test-utimens
test_utimens_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBINTL@ test_utimens_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_NANOSLEEP) @LIBINTL@

View File

@@ -9,8 +9,8 @@ tests/macros.h
Depends-on: Depends-on:
ignore-value ignore-value
nanosleep
timespec timespec
usleep
utimecmp utimecmp
configure.ac: configure.ac:
@@ -18,4 +18,4 @@ configure.ac:
Makefile.am: Makefile.am:
TESTS += test-utimensat TESTS += test-utimensat
check_PROGRAMS += test-utimensat check_PROGRAMS += test-utimensat
test_utimensat_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBINTL@ test_utimensat_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_NANOSLEEP) @LIBINTL@

View File

@@ -19,19 +19,33 @@
#ifndef GLTEST_NAP_H #ifndef GLTEST_NAP_H
# define GLTEST_NAP_H # define GLTEST_NAP_H
static int # include <limits.h>
lt_mtime (struct stat const *a, struct stat const *b)
{
time_t as = a->st_mtime;
time_t bs = b->st_mtime;
int ans = get_stat_mtime_ns (a);
int bns = get_stat_mtime_ns (b);
return as < bs || (as == bs && ans < bns); /* Return A - B, in ns.
Return 0 if the true result would be negative.
Return INT_MAX if the true result would be greater than INT_MAX. */
static int
diff_timespec (struct timespec a, struct timespec b)
{
time_t as = a.tv_sec;
time_t bs = b.tv_sec;
int ans = a.tv_nsec;
int bns = b.tv_nsec;
if (! (bs < as || (bs == as && bns < ans)))
return 0;
if (as - bs <= INT_MAX / 1000000000)
{
int sdiff = (as - bs) * 1000000000;
int usdiff = ans - bns;
if (usdiff < INT_MAX - sdiff)
return sdiff + usdiff;
}
return INT_MAX;
} }
static void static void
get_mtime (int fd, struct stat *st, int do_write) get_stat (int fd, struct stat *st, int do_write)
{ {
if (do_write) if (do_write)
ASSERT (write (fd, "\n", 1) == 1); ASSERT (write (fd, "\n", 1) == 1);
@@ -39,50 +53,61 @@ get_mtime (int fd, struct stat *st, int do_write)
} }
/* Given a file whose descriptor is FD, see whether delaying by DELAY /* Given a file whose descriptor is FD, see whether delaying by DELAY
microseconds causes a change in a file's time stamp. If the time nanoseconds causes a change in a file's time stamp. *ST is the
stamps differ, repeat the test one more time, in case we crossed a file's status, recently gotten. Update *ST to reflect the latest
quantization boundary on a file system with lower resolution. *ST status gotten. If successful, return the needed delay, in
is the file's status, recently gotten. Update *ST to reflect the nanoseconds as determined by the observed time stamps; this may be
latest status gotten. */ greater than DELAY if we crossed a quantization boundary. If
unsuccessful, return 0. */
static int static int
nap_works (int fd, int delay, struct stat *st) nap_works (int fd, int delay, struct stat *st)
{ {
struct stat old_st; struct stat old_st = *st;
old_st = *st; struct timespec delay_spec;
usleep (delay); int cdiff, mdiff;
get_mtime (fd, st, 1); delay_spec.tv_sec = delay / 1000000000;
if (! lt_mtime (&old_st, st)) delay_spec.tv_nsec = delay % 1000000000;
return 0; ASSERT (nanosleep (&delay_spec, 0) == 0);
old_st = *st; get_stat (fd, st, 1);
usleep (delay);
get_mtime (fd, st, 1); /* Return the greater of the ctime and the mtime differences, or
return lt_mtime (&old_st, st); zero if it cannot be determined, or INT_MAX if either overflows. */
cdiff = diff_timespec (get_stat_ctime (st), get_stat_ctime (&old_st));
if (cdiff != 0)
{
mdiff = diff_timespec (get_stat_mtime (st), get_stat_mtime (&old_st));
if (mdiff != 0)
return cdiff < mdiff ? mdiff : cdiff;
}
return 0;
} }
static int static int
guess_delay (void) guess_delay (void)
{ {
/* Try a 1-microsecond sleep first, for speed. If that doesn't /* Try a 1-ns sleep first, for speed. If that doesn't work, try 100
work, try a 1 ms sleep; that should work with ext. If it doesn't ns, 1 microsecond, 1 ms, etc. xfs has a quantization of about 10
work, try a 20 ms sleep. xfs has a quantization of about 10
milliseconds, even though it has a granularity of 1 nanosecond, milliseconds, even though it has a granularity of 1 nanosecond,
and NTFS has a default quantization of 15.25 milliseconds, even and NTFS has a default quantization of 15.25 milliseconds, even
though it has a granularity of 100 nanoseconds, so 20 ms is a though it has a granularity of 100 nanoseconds, so 15.25 ms is a
good quantization to try. If that doesn't work, try 1 second. good quantization to try. If that doesn't work, try 1 second.
The worst case is 2 seconds, needed for FAT. */ The worst case is 2 seconds, needed for FAT. */
static int const delaytab[] = {1, 1000, 20000, 1000000 }; static int const delaytab[] = {1, 1000, 1000000, 15250000, 1000000000 };
int fd = creat (BASE "tmp", 0600); int fd = creat (BASE "tmp", 0600);
int i; int i;
int delay = 2000000; int delay = 2000000000;
struct stat st; struct stat st;
ASSERT (0 <= fd); ASSERT (0 <= fd);
get_mtime (fd, &st, 0); get_stat (fd, &st, 0);
for (i = 0; i < sizeof delaytab / sizeof delaytab[0]; i++) for (i = 0; i < sizeof delaytab / sizeof delaytab[0]; i++)
if (nap_works (fd, delaytab[i], &st)) {
{ int d = nap_works (fd, delaytab[i], &st);
delay = delaytab[i]; if (d != 0)
break; {
} delay = d;
break;
}
}
ASSERT (close (fd) == 0); ASSERT (close (fd) == 0);
ASSERT (unlink (BASE "tmp") == 0); ASSERT (unlink (BASE "tmp") == 0);
return delay; return delay;
@@ -90,14 +115,24 @@ guess_delay (void)
/* Sleep long enough to notice a timestamp difference on the file /* Sleep long enough to notice a timestamp difference on the file
system in the current directory. Assumes that BASE is defined, system in the current directory. Assumes that BASE is defined,
and requires that the test module depends on usleep. */ and requires that the test module depends on nanosleep. */
static void static void
nap (void) nap (void)
{ {
static int delay; static struct timespec delay;
if (!delay) if (!delay.tv_sec && !delay.tv_nsec)
delay = guess_delay (); {
usleep (delay); int d = guess_delay ();
/* Multiply by 1.125 (rounding up), to avoid problems if the
file system's clock is a bit slower than nanosleep's.
Ceiling it at INT_MAX, though. */
int delta = (d >> 3) + ((d & 7) != 0);
d = delta < INT_MAX - d ? d + delta : INT_MAX;
delay.tv_sec = d / 1000000000;
delay.tv_nsec = d % 1000000000;
}
ASSERT (nanosleep (&delay, 0) == 0);
} }
#endif /* GLTEST_NAP_H */ #endif /* GLTEST_NAP_H */

View File

@@ -27,6 +27,9 @@
#include "macros.h" #include "macros.h"
#define BASE "test-stat-time.t"
#include "nap.h"
enum { NFILES = 4 }; enum { NFILES = 4 };
static int static int
@@ -79,48 +82,6 @@ do_stat (const char *filename, struct stat *p)
ASSERT (stat (filename, p) == 0); ASSERT (stat (filename, p) == 0);
} }
/* Sleep long enough to notice a timestamp difference on the file
system in the current directory. */
static void
nap (void)
{
static long delay;
if (!delay)
{
/* Initialize only once, by sleeping for 20 milliseconds (needed
since xfs has a quantization of about 10 milliseconds, even
though it has a granularity of 1 nanosecond, and since NTFS
has a default quantization of 15.25 milliseconds, even though
it has a granularity of 100 nanoseconds). If the seconds
differ, repeat the test one more time (in case we crossed a
quantization boundary on a file system with 1 second
resolution). If we can't observe a difference in only the
nanoseconds, then fall back to 1 second if the time is odd,
and 2 seconds (needed for FAT) if time is even. */
struct stat st1;
struct stat st2;
ASSERT (stat ("t-stt-stamp1", &st1) == 0);
ASSERT (force_unlink ("t-stt-stamp1") == 0);
delay = 20000;
usleep (delay);
create_file ("t-stt-stamp1");
ASSERT (stat ("t-stt-stamp1", &st2) == 0);
if (st1.st_mtime != st2.st_mtime)
{
/* Seconds differ, give it one more shot. */
st1 = st2;
ASSERT (force_unlink ("t-stt-stamp1") == 0);
usleep (delay);
create_file ("t-stt-stamp1");
ASSERT (stat ("t-stt-stamp1", &st2) == 0);
}
if (! (st1.st_mtime == st2.st_mtime
&& get_stat_mtime_ns (&st1) < get_stat_mtime_ns (&st2)))
delay = (st1.st_mtime & 1) ? 1000000 : 2000000;
}
usleep (delay);
}
static void static void
prepare_test (struct stat *statinfo, struct timespec *modtimes) prepare_test (struct stat *statinfo, struct timespec *modtimes)
{ {