[v2,2/6] support: Add timespec-related helper functions

Message ID 1b09fef9f8a0b6955378a9f9c35bc4dd91650289.1554665560.git-series.mac@mcrowe.com
State Committed
Headers

Commit Message

Mike Crowe April 7, 2019, 7:33 p.m. UTC
  * support/timespec.h: New file. Provide timespec helper functions
	along with macros in the style of those in check.h.

	* support/timespec.c: New file. Implement check functions declared
	in support/timespec.h.

	* support/timespec-add.c: New file from gnulib containing
	timespec_add implementation that handles overflow.

	* support/timespec-sub.c: New file from gnulib containing
	timespec_sub implementation that handles overflow.

	* support/README: Mention timespec.h.
---
 ChangeLog              | 16 +++++++++-
 support/Makefile       |  3 ++-
 support/README         |  5 +++-
 support/timespec-add.c | 75 +++++++++++++++++++++++++++++++++++++++++-
 support/timespec-sub.c | 75 +++++++++++++++++++++++++++++++++++++++++-
 support/timespec.c     | 63 ++++++++++++++++++++++++++++++++++-
 support/timespec.h     | 79 +++++++++++++++++++++++++++++++++++++++++++-
 7 files changed, 316 insertions(+)
 create mode 100644 support/timespec-add.c
 create mode 100644 support/timespec-sub.c
 create mode 100644 support/timespec.c
 create mode 100644 support/timespec.h
  

Comments

Adhemerval Zanella April 23, 2019, 5:12 p.m. UTC | #1
On 07/04/2019 16:33, Mike Crowe wrote:
> * support/timespec.h: New file. Provide timespec helper functions
> 	along with macros in the style of those in check.h.
> 
> 	* support/timespec.c: New file. Implement check functions declared
> 	in support/timespec.h.
> 
> 	* support/timespec-add.c: New file from gnulib containing
> 	timespec_add implementation that handles overflow.
> 
> 	* support/timespec-sub.c: New file from gnulib containing
> 	timespec_sub implementation that handles overflow.
> 
> 	* support/README: Mention timespec.h.
> ---
>  ChangeLog              | 16 +++++++++-
>  support/Makefile       |  3 ++-
>  support/README         |  5 +++-
>  support/timespec-add.c | 75 +++++++++++++++++++++++++++++++++++++++++-
>  support/timespec-sub.c | 75 +++++++++++++++++++++++++++++++++++++++++-
>  support/timespec.c     | 63 ++++++++++++++++++++++++++++++++++-
>  support/timespec.h     | 79 +++++++++++++++++++++++++++++++++++++++++++-
>  7 files changed, 316 insertions(+)
>  create mode 100644 support/timespec-add.c
>  create mode 100644 support/timespec-sub.c
>  create mode 100644 support/timespec.c
>  create mode 100644 support/timespec.h
> 
> diff --git a/ChangeLog b/ChangeLog
> index 8c26806..49dc84e 100644
> --- a/ChangeLog
> +++ b/ChangeLog
> @@ -1,5 +1,21 @@
>  2019-04-06  Mike Crowe  <mac@mcrowe.com>
>  
> +	* support/timespec.h: New file. Provide timespec helper functions
> +	along with macros in the style of those in check.h.
> +
> +	* support/timespec.c: New file. Implement check functions declared
> +	in support/timespec.h.
> +
> +	* support/timespec-add.c: New file from gnulib containing
> +	timespec_add implementation that handles overflow.
> +
> +	* support/timespec-sub.c: New file from gnulib containing
> +	timespec_sub implementation that handles overflow.
> +
> +	* support/README: Mention timespec.h.
> +

Ok.

> +2019-04-06  Mike Crowe  <mac@mcrowe.com>
> +
>  	* support/xclock_gettime.c (xclock_gettime): New file. Provide
>  	clock_gettime wrapper for use in tests that fails the test rather
>  	than returning failure.
> diff --git a/support/Makefile b/support/Makefile
> index 1d37f70..94a1416 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -70,6 +70,9 @@ libsupport-routines = \
>    support_test_main \
>    support_test_verify_impl \
>    temp_file \
> +  timespec \
> +  timespec-add \
> +  timespec-sub \
>    write_message \
>    xaccept \
>    xaccept4 \

Ok.

> diff --git a/support/README b/support/README
> index d82f472..e33067e 100644
> --- a/support/README
> +++ b/support/README
> @@ -28,3 +28,8 @@ header files provide related declarations:
>  * check.h
>  * temp_file.h
>  * test-driver.h
> +
> +For tests that make use of struct timespec, the following header file
> +contains additional macros and helper functions:
> +
> +* timespec.h

Ok.

> diff --git a/support/timespec-add.c b/support/timespec-add.c
> new file mode 100644
> index 0000000..523eac8
> --- /dev/null
> +++ b/support/timespec-add.c
> @@ -0,0 +1,75 @@
> +/* Add two struct timespec values.
> +
> +   Copyright (C) 2011-2019 Free Software Foundation, Inc.
> +
> +   This file is part of the GNU C Library, although it originally came
> +   from gnulib.

Based from previous gnulib additions, I think we can use default Glibc
header and maybe added a comment below that it came from gnulib
originally.

> +
> +   The GNU C Library 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.
> +
> +   The GNU C Library 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 the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +/* Written by Paul Eggert.  */

We don't add authorship anymore on new files. I think a better approach
is to comment it came from gnulib to keep track for future syncs.

> +
> +/* Return the sum of two timespec values A and B.  On overflow, return
> +   an extremal value.  This assumes 0 <= tv_nsec < TIMESPEC_HZ.  */
> +
> +#include <config.h>
> +#include "timespec.h"
> +
> +#include "intprops.h"
> +
> +struct timespec
> +timespec_add (struct timespec a, struct timespec b)
> +{
> +  time_t rs = a.tv_sec;
> +  time_t bs = b.tv_sec;
> +  int ns = a.tv_nsec + b.tv_nsec;
> +  int nsd = ns - TIMESPEC_HZ;
> +  int rns = ns;
> +  time_t tmin = TYPE_MINIMUM (time_t);
> +  time_t tmax = TYPE_MAXIMUM (time_t);
> +
> +  if (0 <= nsd)
> +    {
> +      rns = nsd;
> +      if (bs < tmax)
> +        bs++;
> +      else if (rs < 0)
> +        rs++;
> +      else
> +        goto high_overflow;
> +    }
> +
> +  /* INT_ADD_WRAPV is not appropriate since time_t might be unsigned.
> +     In theory time_t might be narrower than int, so plain
> +     INT_ADD_OVERFLOW does not suffice.  */
> +  if (! INT_ADD_OVERFLOW (rs, bs) && tmin <= rs + bs && rs + bs <= tmax)
> +    rs += bs;
> +  else
> +    {
> +      if (rs < 0)
> +        {
> +          rs = tmin;
> +          rns = 0;
> +        }
> +      else
> +        {
> +        high_overflow:
> +          rs = tmax;
> +          rns = TIMESPEC_HZ - 1;
> +        }
> +    }
> +
> +  return make_timespec (rs, rns);
> +}

Ok.

> diff --git a/support/timespec-sub.c b/support/timespec-sub.c
> new file mode 100644
> index 0000000..7e67e87
> --- /dev/null
> +++ b/support/timespec-sub.c
> @@ -0,0 +1,75 @@
> +/* Subtract two struct timespec values.
> +
> +   Copyright (C) 2011-2019 Free Software Foundation, Inc.
> +
> +   This file is part of the GNU C Library, although it originally came
> +   from gnulib.

Same as before.

> +
> +   The GNU C Library 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.
> +
> +   The GNU C Library 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 the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +/* Written by Paul Eggert.  */

Ditto.

> +
> +/* Return the difference between two timespec values A and B.  On
> +   overflow, return an extremal value.  This assumes 0 <= tv_nsec <
> +   TIMESPEC_HZ.  */
> +
> +#include <config.h>
> +#include "timespec.h"
> +
> +#include "intprops.h"
> +
> +struct timespec
> +timespec_sub (struct timespec a, struct timespec b)
> +{
> +  time_t rs = a.tv_sec;
> +  time_t bs = b.tv_sec;
> +  int ns = a.tv_nsec - b.tv_nsec;
> +  int rns = ns;
> +  time_t tmin = TYPE_MINIMUM (time_t);
> +  time_t tmax = TYPE_MAXIMUM (time_t);
> +
> +  if (ns < 0)
> +    {
> +      rns = ns + TIMESPEC_HZ;
> +      if (bs < tmax)
> +        bs++;
> +      else if (- TYPE_SIGNED (time_t) < rs)
> +        rs--;
> +      else
> +        goto low_overflow;
> +    }
> +
> +  /* INT_SUBTRACT_WRAPV is not appropriate since time_t might be unsigned.
> +     In theory time_t might be narrower than int, so plain
> +     INT_SUBTRACT_OVERFLOW does not suffice.  */
> +  if (! INT_SUBTRACT_OVERFLOW (rs, bs) && tmin <= rs - bs && rs - bs <= tmax)
> +    rs -= bs;
> +  else
> +    {
> +      if (rs < 0)
> +        {
> +        low_overflow:
> +          rs = tmin;
> +          rns = 0;
> +        }
> +      else
> +        {
> +          rs = tmax;
> +          rns = TIMESPEC_HZ - 1;
> +        }
> +    }
> +
> +  return make_timespec (rs, rns);
> +}

Ok.

> diff --git a/support/timespec.c b/support/timespec.c
> new file mode 100644
> index 0000000..c34fe73
> --- /dev/null
> +++ b/support/timespec.c
> @@ -0,0 +1,63 @@
> +/* Support code for timespec checks.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library 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.
> +
> +   The GNU C Library 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 the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <support/timespec.h>
> +#include <errno.h>
> +#include <stdio.h>
> +
> +void
> +test_timespec_before_impl (const char *file, int line,
> +			   const struct timespec left,
> +			   const struct timespec right)
> +{
> +  if (left.tv_sec > right.tv_sec
> +      || (left.tv_sec == right.tv_sec
> +	  && left.tv_nsec > right.tv_nsec)) {
> +    const int saved_errno = errno;
> +    support_record_failure ();
> +    const struct timespec diff = timespec_sub (left, right);
> +    printf ("%s:%d: %ld.%09lds not before %ld.%09lds "
> +	    "(difference %ld.%09lds)n",
> +	    file, line,
> +	    left.tv_sec, left.tv_nsec,
> +	    right.tv_sec, right.tv_nsec,
> +	    diff.tv_sec, diff.tv_nsec);
> +    errno = saved_errno;
> +  }
> +}

I think there is no need to save and restore errno, unless you are
actively testing errno after an expected failure (which is not the
case for the patchset and it not usual for libsupport and/or glibc
tests as well).

> +
> +void
> +test_timespec_equal_or_after_impl (const char *file, int line,
> +				   const struct timespec left,
> +				   const struct timespec right)
> +{
> +  if (left.tv_sec < right.tv_sec
> +      || (left.tv_sec == right.tv_sec
> +	  && left.tv_nsec < right.tv_nsec)) {
> +    const int saved_errno = errno;
> +    support_record_failure ();
> +    const struct timespec diff = timespec_sub (right, left);
> +    printf ("%s:%d: %ld.%09lds not after %ld.%09lds "
> +	    "(difference %ld.%09lds)n",
> +	    file, line,
> +	    left.tv_sec, left.tv_nsec,
> +	    right.tv_sec, right.tv_nsec,
> +	    diff.tv_sec, diff.tv_nsec);
> +    errno = saved_errno;
> +  }
> +}

Ditto.

> diff --git a/support/timespec.h b/support/timespec.h
> new file mode 100644
> index 0000000..4a8b341
> --- /dev/null
> +++ b/support/timespec.h
> @@ -0,0 +1,79 @@
> +/* Useful functions for tests that use struct timespec.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library 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.
> +
> +   The GNU C Library 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 the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef SUPPORT_TIMESPEC_H
> +#define SUPPORT_TIMESPEC_H
> +
> +#include <stdio.h>
> +#include <time.h>
> +#include <support/check.h>
> +#include <support/xtime.h>
> +
> +struct timespec timespec_add (struct timespec, struct timespec)
> +  __attribute__((const));
> +struct timespec timespec_sub (struct timespec, struct timespec)
> +  __attribute__((const));
> +
> +static inline struct timespec
> +make_timespec (time_t s, long int ns)
> +{
> +  struct timespec r;
> +  r.tv_sec = s;
> +  r.tv_nsec = ns;
> +  return r;
> +}
> +
> +enum { TIMESPEC_HZ = 1000000000 };
> +
> +void test_timespec_before_impl (const char *file, int line,
> +                                const struct timespec left,
> +                                const struct timespec right);
> +
> +void test_timespec_equal_or_after_impl (const char *file, int line,
> +                                        const struct timespec left,
> +                                        const struct timespec right);
> +
> +/* Check that the timespec on the left represents a time before the
> +   time on the right. */
> +#define TEST_TIMESPEC_BEFORE(left, right)                               \
> +  test_timespec_before_impl (__FILE__, __LINE__, (left), (right))
> +
> +#define TEST_TIMESPEC_BEFORE_NOW(left, clockid)                 \
> +  ({                                                            \
> +    struct timespec now;                                        \
> +    const int saved_errno = errno;                              \
> +    xclock_gettime ((clockid), &now);                           \
> +    TEST_TIMESPEC_BEFORE ((left), now);                         \
> +    errno = saved_errno;                                        \
> +  })

Same as before for errno, I don't see much point on save/restore errno
for tests itself.

> +
> +/* Check that the timespec on the left represents a after before the
> +   time on the right. */
> +#define TEST_TIMESPEC_EQUAL_OR_AFTER(left, right)                       \
> +  test_timespec_equal_or_after_impl (__FILE__, __LINE__, left, right)
> +
> +#define TEST_TIMESPEC_NOW_OR_AFTER(clockid, right)              \
> +  ({                                                            \
> +    struct timespec now;                                        \
> +    const int saved_errno = errno;                              \
> +    xclock_gettime ((clockid), &now);                           \
> +    TEST_TIMESPEC_EQUAL_OR_AFTER (now, (right));                \
> +    errno = saved_errno;                                        \
> +  })
> +
> +#endif /* SUPPORT_TIMESPEC_H */
> 

Same as before.
  
Mike Crowe April 25, 2019, 12:37 p.m. UTC | #2
On Tuesday 23 April 2019 at 14:12:56 -0300, Adhemerval Zanella wrote:
> > diff --git a/support/timespec-add.c b/support/timespec-add.c
> > new file mode 100644
> > index 0000000..523eac8
> > --- /dev/null
> > +++ b/support/timespec-add.c
> > @@ -0,0 +1,75 @@
> > +/* Add two struct timespec values.
> > +
> > +   Copyright (C) 2011-2019 Free Software Foundation, Inc.
> > +
> > +   This file is part of the GNU C Library, although it originally came
> > +   from gnulib.
> 
> Based from previous gnulib additions, I think we can use default Glibc
> header and maybe added a comment below that it came from gnulib
> originally.

posix/getopt.c and other files contain:

 This file is part of the GNU C Library and is also part of gnulib.
 Patches to this file should be submitted to both projects.

followed by the standard GNU C copyright notice. Would that be acceptable?

> > +/* Written by Paul Eggert.  */
> 
> We don't add authorship anymore on new files. I think a better approach
> is to comment it came from gnulib to keep track for future syncs.

OK.

> > diff --git a/support/timespec.c b/support/timespec.c
> > new file mode 100644
> > index 0000000..c34fe73
> > --- /dev/null
> > +++ b/support/timespec.c
[snip]
> > +void
> > +test_timespec_before_impl (const char *file, int line,
> > +			   const struct timespec left,
> > +			   const struct timespec right)
> > +{
> > +  if (left.tv_sec > right.tv_sec
> > +      || (left.tv_sec == right.tv_sec
> > +	  && left.tv_nsec > right.tv_nsec)) {
> > +    const int saved_errno = errno;
> > +    support_record_failure ();
> > +    const struct timespec diff = timespec_sub (left, right);
> > +    printf ("%s:%d: %ld.%09lds not before %ld.%09lds "
> > +	    "(difference %ld.%09lds)n",
> > +	    file, line,
> > +	    left.tv_sec, left.tv_nsec,
> > +	    right.tv_sec, right.tv_nsec,
> > +	    diff.tv_sec, diff.tv_nsec);
> > +    errno = saved_errno;
> > +  }
> > +}
> 
> I think there is no need to save and restore errno, unless you are
> actively testing errno after an expected failure (which is not the
> case for the patchset and it not usual for libsupport and/or glibc
> tests as well).

I can remove that. I only did so because support_test_compare_failure did
so.

Mike.
  

Patch

diff --git a/ChangeLog b/ChangeLog
index 8c26806..49dc84e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@ 
 2019-04-06  Mike Crowe  <mac@mcrowe.com>
 
+	* support/timespec.h: New file. Provide timespec helper functions
+	along with macros in the style of those in check.h.
+
+	* support/timespec.c: New file. Implement check functions declared
+	in support/timespec.h.
+
+	* support/timespec-add.c: New file from gnulib containing
+	timespec_add implementation that handles overflow.
+
+	* support/timespec-sub.c: New file from gnulib containing
+	timespec_sub implementation that handles overflow.
+
+	* support/README: Mention timespec.h.
+
+2019-04-06  Mike Crowe  <mac@mcrowe.com>
+
 	* support/xclock_gettime.c (xclock_gettime): New file. Provide
 	clock_gettime wrapper for use in tests that fails the test rather
 	than returning failure.
diff --git a/support/Makefile b/support/Makefile
index 1d37f70..94a1416 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -70,6 +70,9 @@  libsupport-routines = \
   support_test_main \
   support_test_verify_impl \
   temp_file \
+  timespec \
+  timespec-add \
+  timespec-sub \
   write_message \
   xaccept \
   xaccept4 \
diff --git a/support/README b/support/README
index d82f472..e33067e 100644
--- a/support/README
+++ b/support/README
@@ -28,3 +28,8 @@  header files provide related declarations:
 * check.h
 * temp_file.h
 * test-driver.h
+
+For tests that make use of struct timespec, the following header file
+contains additional macros and helper functions:
+
+* timespec.h
diff --git a/support/timespec-add.c b/support/timespec-add.c
new file mode 100644
index 0000000..523eac8
--- /dev/null
+++ b/support/timespec-add.c
@@ -0,0 +1,75 @@ 
+/* Add two struct timespec values.
+
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library, although it originally came
+   from gnulib.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+/* Return the sum of two timespec values A and B.  On overflow, return
+   an extremal value.  This assumes 0 <= tv_nsec < TIMESPEC_HZ.  */
+
+#include <config.h>
+#include "timespec.h"
+
+#include "intprops.h"
+
+struct timespec
+timespec_add (struct timespec a, struct timespec b)
+{
+  time_t rs = a.tv_sec;
+  time_t bs = b.tv_sec;
+  int ns = a.tv_nsec + b.tv_nsec;
+  int nsd = ns - TIMESPEC_HZ;
+  int rns = ns;
+  time_t tmin = TYPE_MINIMUM (time_t);
+  time_t tmax = TYPE_MAXIMUM (time_t);
+
+  if (0 <= nsd)
+    {
+      rns = nsd;
+      if (bs < tmax)
+        bs++;
+      else if (rs < 0)
+        rs++;
+      else
+        goto high_overflow;
+    }
+
+  /* INT_ADD_WRAPV is not appropriate since time_t might be unsigned.
+     In theory time_t might be narrower than int, so plain
+     INT_ADD_OVERFLOW does not suffice.  */
+  if (! INT_ADD_OVERFLOW (rs, bs) && tmin <= rs + bs && rs + bs <= tmax)
+    rs += bs;
+  else
+    {
+      if (rs < 0)
+        {
+          rs = tmin;
+          rns = 0;
+        }
+      else
+        {
+        high_overflow:
+          rs = tmax;
+          rns = TIMESPEC_HZ - 1;
+        }
+    }
+
+  return make_timespec (rs, rns);
+}
diff --git a/support/timespec-sub.c b/support/timespec-sub.c
new file mode 100644
index 0000000..7e67e87
--- /dev/null
+++ b/support/timespec-sub.c
@@ -0,0 +1,75 @@ 
+/* Subtract two struct timespec values.
+
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library, although it originally came
+   from gnulib.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+/* Return the difference between two timespec values A and B.  On
+   overflow, return an extremal value.  This assumes 0 <= tv_nsec <
+   TIMESPEC_HZ.  */
+
+#include <config.h>
+#include "timespec.h"
+
+#include "intprops.h"
+
+struct timespec
+timespec_sub (struct timespec a, struct timespec b)
+{
+  time_t rs = a.tv_sec;
+  time_t bs = b.tv_sec;
+  int ns = a.tv_nsec - b.tv_nsec;
+  int rns = ns;
+  time_t tmin = TYPE_MINIMUM (time_t);
+  time_t tmax = TYPE_MAXIMUM (time_t);
+
+  if (ns < 0)
+    {
+      rns = ns + TIMESPEC_HZ;
+      if (bs < tmax)
+        bs++;
+      else if (- TYPE_SIGNED (time_t) < rs)
+        rs--;
+      else
+        goto low_overflow;
+    }
+
+  /* INT_SUBTRACT_WRAPV is not appropriate since time_t might be unsigned.
+     In theory time_t might be narrower than int, so plain
+     INT_SUBTRACT_OVERFLOW does not suffice.  */
+  if (! INT_SUBTRACT_OVERFLOW (rs, bs) && tmin <= rs - bs && rs - bs <= tmax)
+    rs -= bs;
+  else
+    {
+      if (rs < 0)
+        {
+        low_overflow:
+          rs = tmin;
+          rns = 0;
+        }
+      else
+        {
+          rs = tmax;
+          rns = TIMESPEC_HZ - 1;
+        }
+    }
+
+  return make_timespec (rs, rns);
+}
diff --git a/support/timespec.c b/support/timespec.c
new file mode 100644
index 0000000..c34fe73
--- /dev/null
+++ b/support/timespec.c
@@ -0,0 +1,63 @@ 
+/* Support code for timespec checks.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/timespec.h>
+#include <errno.h>
+#include <stdio.h>
+
+void
+test_timespec_before_impl (const char *file, int line,
+			   const struct timespec left,
+			   const struct timespec right)
+{
+  if (left.tv_sec > right.tv_sec
+      || (left.tv_sec == right.tv_sec
+	  && left.tv_nsec > right.tv_nsec)) {
+    const int saved_errno = errno;
+    support_record_failure ();
+    const struct timespec diff = timespec_sub (left, right);
+    printf ("%s:%d: %ld.%09lds not before %ld.%09lds "
+	    "(difference %ld.%09lds)n",
+	    file, line,
+	    left.tv_sec, left.tv_nsec,
+	    right.tv_sec, right.tv_nsec,
+	    diff.tv_sec, diff.tv_nsec);
+    errno = saved_errno;
+  }
+}
+
+void
+test_timespec_equal_or_after_impl (const char *file, int line,
+				   const struct timespec left,
+				   const struct timespec right)
+{
+  if (left.tv_sec < right.tv_sec
+      || (left.tv_sec == right.tv_sec
+	  && left.tv_nsec < right.tv_nsec)) {
+    const int saved_errno = errno;
+    support_record_failure ();
+    const struct timespec diff = timespec_sub (right, left);
+    printf ("%s:%d: %ld.%09lds not after %ld.%09lds "
+	    "(difference %ld.%09lds)n",
+	    file, line,
+	    left.tv_sec, left.tv_nsec,
+	    right.tv_sec, right.tv_nsec,
+	    diff.tv_sec, diff.tv_nsec);
+    errno = saved_errno;
+  }
+}
diff --git a/support/timespec.h b/support/timespec.h
new file mode 100644
index 0000000..4a8b341
--- /dev/null
+++ b/support/timespec.h
@@ -0,0 +1,79 @@ 
+/* Useful functions for tests that use struct timespec.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef SUPPORT_TIMESPEC_H
+#define SUPPORT_TIMESPEC_H
+
+#include <stdio.h>
+#include <time.h>
+#include <support/check.h>
+#include <support/xtime.h>
+
+struct timespec timespec_add (struct timespec, struct timespec)
+  __attribute__((const));
+struct timespec timespec_sub (struct timespec, struct timespec)
+  __attribute__((const));
+
+static inline struct timespec
+make_timespec (time_t s, long int ns)
+{
+  struct timespec r;
+  r.tv_sec = s;
+  r.tv_nsec = ns;
+  return r;
+}
+
+enum { TIMESPEC_HZ = 1000000000 };
+
+void test_timespec_before_impl (const char *file, int line,
+                                const struct timespec left,
+                                const struct timespec right);
+
+void test_timespec_equal_or_after_impl (const char *file, int line,
+                                        const struct timespec left,
+                                        const struct timespec right);
+
+/* Check that the timespec on the left represents a time before the
+   time on the right. */
+#define TEST_TIMESPEC_BEFORE(left, right)                               \
+  test_timespec_before_impl (__FILE__, __LINE__, (left), (right))
+
+#define TEST_TIMESPEC_BEFORE_NOW(left, clockid)                 \
+  ({                                                            \
+    struct timespec now;                                        \
+    const int saved_errno = errno;                              \
+    xclock_gettime ((clockid), &now);                           \
+    TEST_TIMESPEC_BEFORE ((left), now);                         \
+    errno = saved_errno;                                        \
+  })
+
+/* Check that the timespec on the left represents a after before the
+   time on the right. */
+#define TEST_TIMESPEC_EQUAL_OR_AFTER(left, right)                       \
+  test_timespec_equal_or_after_impl (__FILE__, __LINE__, left, right)
+
+#define TEST_TIMESPEC_NOW_OR_AFTER(clockid, right)              \
+  ({                                                            \
+    struct timespec now;                                        \
+    const int saved_errno = errno;                              \
+    xclock_gettime ((clockid), &now);                           \
+    TEST_TIMESPEC_EQUAL_OR_AFTER (now, (right));                \
+    errno = saved_errno;                                        \
+  })
+
+#endif /* SUPPORT_TIMESPEC_H */