This set of patches introduces a new module 'time', that works around the inconsistency of time(NULL) with gettimeofday().tv_sec and timespec_get().tv_sec on glibc systems.
The replacement is a bit slower than the native glibc time(NULL) implementation — 31 nanoseconds instead of 17 nanoseconds, according to Florian Weimer's measurements <https://sourceware.org/pipermail/libc-alpha/2023-March/146133.html>. The new module is for those GNU programs that prefer to avoid trouble due to clocks apparently going backwards. As a bonus, it allows us to fix the test failures of the gettimeofday and timespec_get tests that occurred with a probability between 0.1% and 0.3% on glibc/Linux. 2023-03-08 Bruno Haible <br...@clisp.org> gettimeofday, timespec_get tests: Avoid test failure on glibc/Linux. * modules/gettimeofday-tests (Depends-on): Add 'time'. * modules/timespec_get-tests (Depends-on): Likewise. * tests/test-gettimeofday.c (test_consistency): Update comment. * tests/test-timespec_get.c (main): Likewise. time: Add tests. * tests/test-time.c: New file. * modules/time-tests: New file. time: New module. * lib/time.in.h (time): New declaration. * lib/time.c: New file. * m4/time_h.m4 (gl_TIME_H_REQUIRE_DEFAULTS): Initialize GNULIB_TIME. (gl_TIME_H_DEFAULTS): Initialize REPLACE_TIME. * m4/time.m4: New file. * modules/time-h (Makefile.am): Substitute GNULIB_TIME, REPLACE_TIME. * modules/time: New file. * tests/test-time-h-c++.cc: Check the signature of GNULIB_NAMESPACE::time. * doc/posix-functions/time.texi: Mention the glibc problem and the 'time' module.
>From cbc0c1cda32bbcfab3ed0391cb9cfde444323571 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 8 Mar 2023 17:00:48 +0100 Subject: [PATCH 1/3] time: New module. * lib/time.in.h (time): New declaration. * lib/time.c: New file. * m4/time_h.m4 (gl_TIME_H_REQUIRE_DEFAULTS): Initialize GNULIB_TIME. (gl_TIME_H_DEFAULTS): Initialize REPLACE_TIME. * m4/time.m4: New file. * modules/time-h (Makefile.am): Substitute GNULIB_TIME, REPLACE_TIME. * modules/time: New file. * tests/test-time-h-c++.cc: Check the signature of GNULIB_NAMESPACE::time. * doc/posix-functions/time.texi: Mention the glibc problem and the 'time' module. --- ChangeLog | 15 ++++++++++++ doc/posix-functions/time.texi | 10 +++++++- lib/time.c | 41 +++++++++++++++++++++++++++++++++ lib/time.in.h | 14 ++++++++++++ m4/time.m4 | 43 +++++++++++++++++++++++++++++++++++ m4/time_h.m4 | 4 +++- modules/time | 31 +++++++++++++++++++++++++ modules/time-h | 2 ++ tests/test-time-h-c++.cc | 4 ++++ 9 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 lib/time.c create mode 100644 m4/time.m4 create mode 100644 modules/time diff --git a/ChangeLog b/ChangeLog index aac31c31a5..d65b3393c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2023-03-08 Bruno Haible <br...@clisp.org> + + time: New module. + * lib/time.in.h (time): New declaration. + * lib/time.c: New file. + * m4/time_h.m4 (gl_TIME_H_REQUIRE_DEFAULTS): Initialize GNULIB_TIME. + (gl_TIME_H_DEFAULTS): Initialize REPLACE_TIME. + * m4/time.m4: New file. + * modules/time-h (Makefile.am): Substitute GNULIB_TIME, REPLACE_TIME. + * modules/time: New file. + * tests/test-time-h-c++.cc: Check the signature of + GNULIB_NAMESPACE::time. + * doc/posix-functions/time.texi: Mention the glibc problem and the + 'time' module. + 2023-03-08 Bruno Haible <br...@clisp.org> time-h: Renamed from time. diff --git a/doc/posix-functions/time.texi b/doc/posix-functions/time.texi index 765de7aa2a..a8e9abcddf 100644 --- a/doc/posix-functions/time.texi +++ b/doc/posix-functions/time.texi @@ -4,10 +4,18 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html} -Gnulib module: --- +Gnulib module: time Portability problems fixed by Gnulib: @itemize +@item +This function is not consistent with @code{gettimeofday} and @code{timespec_get} +on some platforms: +@c https://sourceware.org/bugzilla/show_bug.cgi?id=30200 +glibc 2.31 or newer on Linux. +Namely, in the first 1 to 2.5 milliseconds of every second, @code{time} +returns a value that is one less than the @code{tv_sec} part of the return +value of @code{gettimeofday} or @code{timespec_get}. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/time.c b/lib/time.c new file mode 100644 index 0000000000..4e2ee31b48 --- /dev/null +++ b/lib/time.c @@ -0,0 +1,41 @@ +/* Provide time() for systems for which it's broken. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible. */ + +#include <config.h> + +/* Specification. */ +#include <time.h> + +#include <stdlib.h> +#include <sys/time.h> + +time_t +time (time_t *tp) +{ + struct timeval tv; + time_t tt; + + if (gettimeofday (&tv, NULL) < 0) + abort (); + tt = tv.tv_sec; + + if (tp) + *tp = tt; + + return tt; +} diff --git a/lib/time.in.h b/lib/time.in.h index 87cda21413..3f9af920e3 100644 --- a/lib/time.in.h +++ b/lib/time.in.h @@ -143,6 +143,20 @@ _GL_CXXALIAS_SYS (timespec_getres, int, (struct timespec *ts, int base)); _GL_CXXALIASWARN (timespec_getres); # endif +/* Return the number of seconds that have elapsed since the Epoch. */ +# if @GNULIB_TIME@ +# if @REPLACE_TIME@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# define time rpl_time +# endif +_GL_FUNCDECL_RPL (time, time_t, (time_t *__tp)); +_GL_CXXALIAS_RPL (time, time_t, (time_t *__tp)); +# else +_GL_CXXALIAS_SYS (time, time_t, (time_t *__tp)); +# endif +_GL_CXXALIASWARN (time); +# endif + /* Sleep for at least RQTP seconds unless interrupted, If interrupted, return -1 and store the remaining time into RMTP. See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/nanosleep.html>. */ diff --git a/m4/time.m4 b/m4/time.m4 new file mode 100644 index 0000000000..0dbb6011ed --- /dev/null +++ b/m4/time.m4 @@ -0,0 +1,43 @@ +# time.m4 serial 1 +dnl Copyright (C) 2023 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gl_FUNC_TIME], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + dnl glibc has the bug https://sourceware.org/bugzilla/show_bug.cgi?id=30200 . + AC_CACHE_CHECK([whether time() works], + [gl_cv_func_time_works], + [dnl Guess that it works except on glibc >= 2.31 with Linux. + dnl And binaries produced on glibc < 2.31 need to run fine on newer + dnl glibc versions as well; therefore ignore __GLIBC_MINOR__. + case "$host_os" in + linux*-gnu*) + AC_EGREP_CPP([Unlucky], [ + #include <features.h> + #ifdef __GNU_LIBRARY__ + #if __GLIBC__ == 2 + Unlucky GNU user + #endif + #endif + ], + [gl_cv_func_time_works="guessing no"], + [gl_cv_func_time_works="guessing yes"]) + ;; + *) gl_cv_func_time_works="guessing yes";; + esac + ]) + case "$gl_cv_func_time_works" in + *no) REPLACE_TIME=1 ;; + esac +]) + +# Prerequisites of lib/time.c. +AC_DEFUN([gl_PREREQ_TIME], +[ + : +]) diff --git a/m4/time_h.m4 b/m4/time_h.m4 index b74870c3d0..51d553a2f1 100644 --- a/m4/time_h.m4 +++ b/m4/time_h.m4 @@ -2,7 +2,7 @@ # Copyright (C) 2000-2001, 2003-2007, 2009-2023 Free Software Foundation, Inc. -# serial 21 +# serial 22 # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -137,6 +137,7 @@ AC_DEFUN([gl_TIME_H_REQUIRE_DEFAULTS] gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_NANOSLEEP]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRFTIME]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRPTIME]) + gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TIME]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TIMEGM]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TIMESPEC_GET]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TIMESPEC_GETRES]) @@ -169,6 +170,7 @@ AC_DEFUN([gl_TIME_H_DEFAULTS] REPLACE_MKTIME=GNULIB_PORTCHECK; AC_SUBST([REPLACE_MKTIME]) REPLACE_NANOSLEEP=GNULIB_PORTCHECK; AC_SUBST([REPLACE_NANOSLEEP]) REPLACE_STRFTIME=GNULIB_PORTCHECK; AC_SUBST([REPLACE_STRFTIME]) + REPLACE_TIME=0; AC_SUBST([REPLACE_TIME]) REPLACE_TIMEGM=GNULIB_PORTCHECK; AC_SUBST([REPLACE_TIMEGM]) REPLACE_TIMESPEC_GET=GNULIB_PORTCHECK; AC_SUBST([REPLACE_TIMESPEC_GET]) REPLACE_TZSET=GNULIB_PORTCHECK; AC_SUBST([REPLACE_TZSET]) diff --git a/modules/time b/modules/time new file mode 100644 index 0000000000..45f5ffd35f --- /dev/null +++ b/modules/time @@ -0,0 +1,31 @@ +Description: +time() function: return current time. + +Files: +lib/time.c +m4/time.m4 + +Depends-on: +time-h + +configure.ac: +gl_FUNC_TIME +gl_CONDITIONAL([GL_COND_OBJ_TIME], [test $REPLACE_TIME = 1]) +AM_COND_IF([GL_COND_OBJ_TIME], [ + gl_PREREQ_TIME +]) +gl_TIME_MODULE_INDICATOR([time]) + +Makefile.am: +if GL_COND_OBJ_TIME +lib_SOURCES += time.c +endif + +Include: +<time.h> + +License: +LGPLv2+ + +Maintainer: +all diff --git a/modules/time-h b/modules/time-h index 75ff3a4d8c..5b0fc3feae 100644 --- a/modules/time-h +++ b/modules/time-h @@ -38,6 +38,7 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $( -e 's/@''GNULIB_NANOSLEEP''@/$(GNULIB_NANOSLEEP)/g' \ -e 's/@''GNULIB_STRFTIME''@/$(GNULIB_STRFTIME)/g' \ -e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \ + -e 's/@''GNULIB_TIME''@/$(GNULIB_TIME)/g' \ -e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \ -e 's/@''GNULIB_TIMESPEC_GET''@/$(GNULIB_TIMESPEC_GET)/g' \ -e 's/@''GNULIB_TIMESPEC_GETRES''@/$(GNULIB_TIMESPEC_GETRES)/g' \ @@ -59,6 +60,7 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $( -e 's|@''REPLACE_MKTIME''@|$(REPLACE_MKTIME)|g' \ -e 's|@''REPLACE_NANOSLEEP''@|$(REPLACE_NANOSLEEP)|g' \ -e 's|@''REPLACE_STRFTIME''@|$(REPLACE_STRFTIME)|g' \ + -e 's|@''REPLACE_TIME''@|$(REPLACE_TIME)|g' \ -e 's|@''REPLACE_TIMEGM''@|$(REPLACE_TIMEGM)|g' \ -e 's|@''REPLACE_TIMESPEC_GET''@|$(REPLACE_TIMESPEC_GET)|g' \ -e 's|@''REPLACE_TZSET''@|$(REPLACE_TZSET)|g' \ diff --git a/tests/test-time-h-c++.cc b/tests/test-time-h-c++.cc index 70b2a0f0c7..13cb436e1b 100644 --- a/tests/test-time-h-c++.cc +++ b/tests/test-time-h-c++.cc @@ -28,6 +28,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::timespec_get, int, (struct timespec *, int)); #endif +#if GNULIB_TEST_TIME +SIGNATURE_CHECK (GNULIB_NAMESPACE::time, time_t, (time_t *)); +#endif + #if GNULIB_TEST_NANOSLEEP SIGNATURE_CHECK (GNULIB_NAMESPACE::nanosleep, int, (struct timespec const *, struct timespec *)); -- 2.34.1
>From 8e356e3d3dd556b2716216fdc7a1bbafe29c8ccb Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 8 Mar 2023 17:02:38 +0100 Subject: [PATCH 2/3] time: Add tests. * tests/test-time.c: New file. * modules/time-tests: New file. --- ChangeLog | 4 ++++ modules/time-tests | 13 ++++++++++++ tests/test-time.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 modules/time-tests create mode 100644 tests/test-time.c diff --git a/ChangeLog b/ChangeLog index d65b3393c4..580b78c331 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-03-08 Bruno Haible <br...@clisp.org> + time: Add tests. + * tests/test-time.c: New file. + * modules/time-tests: New file. + time: New module. * lib/time.in.h (time): New declaration. * lib/time.c: New file. diff --git a/modules/time-tests b/modules/time-tests new file mode 100644 index 0000000000..e817f332cb --- /dev/null +++ b/modules/time-tests @@ -0,0 +1,13 @@ +Files: +tests/test-time.c +tests/signature.h +tests/macros.h + +Depends-on: +gettimeofday + +configure.ac: + +Makefile.am: +TESTS += test-time +check_PROGRAMS += test-time diff --git a/tests/test-time.c b/tests/test-time.c new file mode 100644 index 0000000000..3986ee2f3d --- /dev/null +++ b/tests/test-time.c @@ -0,0 +1,49 @@ +/* Test of time() function. + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible. */ + +#include <config.h> + +#include <time.h> + +#include "signature.h" +SIGNATURE_CHECK (time, time_t, (time_t *)); + +#include <sys/time.h> + +#include "macros.h" + +int +main (void) +{ + /* Check consistency of time() with gettimeofday().tv_sec. */ + struct timeval tv1; + struct timeval tv2; + time_t tt3; + + /* Wait until gettimeofday() reports an increase in tv_sec. */ + ASSERT (gettimeofday (&tv1, NULL) == 0); + do + ASSERT (gettimeofday (&tv2, NULL) == 0); + while (tv2.tv_sec == tv1.tv_sec); + /* We are now at the beginning of a second. Test whether time() reports + the new second or the previous one. */ + tt3 = time (NULL); + ASSERT (tt3 >= tv2.tv_sec); + + return 0; +} -- 2.34.1
>From 570d7dbfff17995c9999ec9ab3d7296d960f1e46 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 8 Mar 2023 17:09:37 +0100 Subject: [PATCH 3/3] gettimeofday, timespec_get tests: Avoid test failure on glibc/Linux. * modules/gettimeofday-tests (Depends-on): Add 'time'. * modules/timespec_get-tests (Depends-on): Likewise. * tests/test-gettimeofday.c (test_consistency): Update comment. * tests/test-timespec_get.c (main): Likewise. --- ChangeLog | 6 ++++++ modules/gettimeofday-tests | 1 + modules/timespec_get-tests | 1 + tests/test-gettimeofday.c | 3 ++- tests/test-timespec_get.c | 3 ++- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 580b78c331..fc3bf8e087 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2023-03-08 Bruno Haible <br...@clisp.org> + gettimeofday, timespec_get tests: Avoid test failure on glibc/Linux. + * modules/gettimeofday-tests (Depends-on): Add 'time'. + * modules/timespec_get-tests (Depends-on): Likewise. + * tests/test-gettimeofday.c (test_consistency): Update comment. + * tests/test-timespec_get.c (main): Likewise. + time: Add tests. * tests/test-time.c: New file. * modules/time-tests: New file. diff --git a/modules/gettimeofday-tests b/modules/gettimeofday-tests index 83c7c4b363..a08631d836 100644 --- a/modules/gettimeofday-tests +++ b/modules/gettimeofday-tests @@ -4,6 +4,7 @@ tests/signature.h tests/macros.h Depends-on: +time configure.ac: diff --git a/modules/timespec_get-tests b/modules/timespec_get-tests index 7784792196..bb11062cb0 100644 --- a/modules/timespec_get-tests +++ b/modules/timespec_get-tests @@ -4,6 +4,7 @@ tests/signature.h tests/macros.h Depends-on: +time configure.ac: diff --git a/tests/test-gettimeofday.c b/tests/test-gettimeofday.c index 6daf069339..d1d5206709 100644 --- a/tests/test-gettimeofday.c +++ b/tests/test-gettimeofday.c @@ -70,7 +70,8 @@ test_consistency () ASSERT (tt2 <= tt4); /* Verify that the tv_sec field of the result is the same as time(NULL). */ - /* Note: This assertion sometimes fails on glibc systems, see + /* Note: It's here that the dependency to the 'time' module is needed. + Without it, this assertion would sometimes fail on glibc systems, see https://sourceware.org/bugzilla/show_bug.cgi?id=30200 */ ASSERT (tv1.tv_sec <= tt2); ASSERT (tt2 <= tv3.tv_sec); diff --git a/tests/test-timespec_get.c b/tests/test-timespec_get.c index 69b98c5c64..a7e927cf62 100644 --- a/tests/test-timespec_get.c +++ b/tests/test-timespec_get.c @@ -46,7 +46,8 @@ main (void) ASSERT (tt2 <= tt4); /* Verify that the tv_sec field of the result is the same as time(NULL). */ - /* Note: This assertion sometimes fails on glibc systems, see + /* Note: It's here that the dependency to the 'time' module is needed. + Without it, this assertion would sometimes fail on glibc systems, see https://sourceware.org/bugzilla/show_bug.cgi?id=30200 */ ASSERT (ts1.tv_sec <= tt2); ASSERT (tt2 <= ts3.tv_sec); -- 2.34.1