Message ID | ccd351f58a9cf8f6ed50ac99da741857d09e260a.1556830454.git-series.mac@mcrowe.com |
---|---|
State | New |
Headers | show |
Series | Add new helper functions+macros to libsupport and use them in some nptl tests | expand |
On 02/05/2019 17:54, 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. > > * nptl/tst-cond11.c: Use libsupport. Still LGTM. I will push it for you. > --- > ChangeLog | 18 ++++- > nptl/tst-cond11.c | 169 ++++++++---------------------------------- > support/Makefile | 3 +- > support/README | 5 +- > support/timespec-add.c | 71 ++++++++++++++++++- > support/timespec-sub.c | 71 ++++++++++++++++++- > support/timespec.c | 58 ++++++++++++++- > support/timespec.h | 79 ++++++++++++++++++++- > 8 files changed, 341 insertions(+), 133 deletions(-) > 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 6008265..cd0ef83 100644 > --- a/ChangeLog > +++ b/ChangeLog > @@ -1,5 +1,23 @@ > 2019-05-02 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. > + > + * nptl/tst-cond11.c: Use libsupport. > + > +2019-05-02 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/nptl/tst-cond11.c b/nptl/tst-cond11.c > index 97a8bd0..3bc4ff4 100644 > --- a/nptl/tst-cond11.c > +++ b/nptl/tst-cond11.c > @@ -21,6 +21,10 @@ > #include <stdio.h> > #include <time.h> > #include <unistd.h> > +#include <support/check.h> > +#include <support/timespec.h> > +#include <support/xthread.h> > +#include <support/xtime.h> > > > #if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0 > @@ -34,132 +38,36 @@ run_test (clockid_t cl) > > printf ("clock = %d\n", (int) cl); > > - if (pthread_condattr_init (&condattr) != 0) > - { > - puts ("condattr_init failed"); > - return 1; > - } > - > - if (pthread_condattr_setclock (&condattr, cl) != 0) > - { > - puts ("condattr_setclock failed"); > - return 1; > - } > + TEST_COMPARE (pthread_condattr_init (&condattr), 0); > + TEST_COMPARE (pthread_condattr_setclock (&condattr, cl), 0); > > clockid_t cl2; > - if (pthread_condattr_getclock (&condattr, &cl2) != 0) > - { > - puts ("condattr_getclock failed"); > - return 1; > - } > - if (cl != cl2) > - { > - printf ("condattr_getclock returned wrong value: %d, expected %d\n", > - (int) cl2, (int) cl); > - return 1; > - } > - > - if (pthread_cond_init (&cond, &condattr) != 0) > - { > - puts ("cond_init failed"); > - return 1; > - } > - > - if (pthread_condattr_destroy (&condattr) != 0) > - { > - puts ("condattr_destroy failed"); > - return 1; > - } > - > - if (pthread_mutexattr_init (&mutattr) != 0) > - { > - puts ("mutexattr_init failed"); > - return 1; > - } > - > - if (pthread_mutexattr_settype (&mutattr, PTHREAD_MUTEX_ERRORCHECK) != 0) > - { > - puts ("mutexattr_settype failed"); > - return 1; > - } > - > - if (pthread_mutex_init (&mut, &mutattr) != 0) > - { > - puts ("mutex_init failed"); > - return 1; > - } > - > - if (pthread_mutexattr_destroy (&mutattr) != 0) > - { > - puts ("mutexattr_destroy failed"); > - return 1; > - } > - > - if (pthread_mutex_lock (&mut) != 0) > - { > - puts ("mutex_lock failed"); > - return 1; > - } > - > - if (pthread_mutex_lock (&mut) != EDEADLK) > - { > - puts ("2nd mutex_lock did not return EDEADLK"); > - return 1; > - } > - > - struct timespec ts; > - if (clock_gettime (cl, &ts) != 0) > - { > - puts ("clock_gettime failed"); > - return 1; > - } > + TEST_COMPARE (pthread_condattr_getclock (&condattr, &cl2), 0); > + TEST_COMPARE (cl, cl2); > + > + TEST_COMPARE (pthread_cond_init (&cond, &condattr), 0); > + TEST_COMPARE (pthread_condattr_destroy (&condattr), 0); > + > + xpthread_mutexattr_init (&mutattr); > + xpthread_mutexattr_settype (&mutattr, PTHREAD_MUTEX_ERRORCHECK); > + xpthread_mutex_init (&mut, &mutattr); > + xpthread_mutexattr_destroy (&mutattr); > + > + xpthread_mutex_lock (&mut); > + TEST_COMPARE (pthread_mutex_lock (&mut), EDEADLK); > + > + struct timespec ts_timeout; > + xclock_gettime (cl, &ts_timeout); > > /* Wait one second. */ > - ++ts.tv_sec; > - > - int e = pthread_cond_timedwait (&cond, &mut, &ts); > - if (e == 0) > - { > - puts ("cond_timedwait succeeded"); > - return 1; > - } > - else if (e != ETIMEDOUT) > - { > - puts ("cond_timedwait did not return ETIMEDOUT"); > - return 1; > - } > - > - struct timespec ts2; > - if (clock_gettime (cl, &ts2) != 0) > - { > - puts ("second clock_gettime failed"); > - return 1; > - } > - > - if (ts2.tv_sec < ts.tv_sec > - || (ts2.tv_sec == ts.tv_sec && ts2.tv_nsec < ts.tv_nsec)) > - { > - puts ("timeout too short"); > - return 1; > - } > - > - if (pthread_mutex_unlock (&mut) != 0) > - { > - puts ("mutex_unlock failed"); > - return 1; > - } > - > - if (pthread_mutex_destroy (&mut) != 0) > - { > - puts ("mutex_destroy failed"); > - return 1; > - } > - > - if (pthread_cond_destroy (&cond) != 0) > - { > - puts ("cond_destroy failed"); > - return 1; > - } > + ++ts_timeout.tv_sec; > + > + TEST_COMPARE (pthread_cond_timedwait (&cond, &mut, &ts_timeout), ETIMEDOUT); > + TEST_TIMESPEC_BEFORE_NOW (ts_timeout, cl); > + > + xpthread_mutex_unlock (&mut); > + xpthread_mutex_destroy (&mut); > + TEST_COMPARE (pthread_cond_destroy (&cond), 0); > > return 0; > } > @@ -171,12 +79,11 @@ do_test (void) > { > #if !defined _POSIX_CLOCK_SELECTION || _POSIX_CLOCK_SELECTION == -1 > > - puts ("_POSIX_CLOCK_SELECTION not supported, test skipped"); > - return 0; > + FAIL_UNSUPPORTED ("_POSIX_CLOCK_SELECTION not supported, test skipped"); > > #else > > - int res = run_test (CLOCK_REALTIME); > + run_test (CLOCK_REALTIME); > > # if defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0 > # if _POSIX_MONOTONIC_CLOCK == 0 > @@ -184,20 +91,16 @@ do_test (void) > if (e < 0) > puts ("CLOCK_MONOTONIC not supported"); > else if (e == 0) > - { > - puts ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0"); > - res = 1; > - } > + FAIL_RET ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0"); > else > # endif > - res |= run_test (CLOCK_MONOTONIC); > + run_test (CLOCK_MONOTONIC); > # else > puts ("_POSIX_MONOTONIC_CLOCK not defined"); > # endif > > - return res; > + return 0; > #endif > } > > -#define TEST_FUNCTION do_test () > -#include "../test-skeleton.c" > +#include <support/test-driver.c> > diff --git a/support/Makefile b/support/Makefile > index 05865fe..64044f6 100644 > --- a/support/Makefile > +++ b/support/Makefile > @@ -71,6 +71,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..ae2c41c 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 files > +contain 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..be7a590 > --- /dev/null > +++ b/support/timespec-add.c > @@ -0,0 +1,71 @@ > +/* Add two struct timespec values. > + Copyright (C) 2011-2019 Free Software Foundation, Inc. > + 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. > + > + 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/>. */ > + > +/* 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..bf95c7f > --- /dev/null > +++ b/support/timespec-sub.c > @@ -0,0 +1,71 @@ > +/* Subtract two struct timespec values. > + Copyright (C) 2011-2019 Free Software Foundation, Inc. > + 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. > + > + 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/>. */ > + > +/* 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..f1b88c1 > --- /dev/null > +++ b/support/timespec.c > @@ -0,0 +1,58 @@ > +/* 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 <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)) { > + 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); > + } > +} > + > +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)) { > + 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); > + } > +} > 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 */ >
* Adhemerval Zanella: >> support/Makefile | 3 +- >> support/README | 5 +- >> support/timespec-add.c | 71 ++++++++++++++++++- >> support/timespec-sub.c | 71 ++++++++++++++++++- >> support/timespec.c | 58 ++++++++++++++- >> support/timespec.h | 79 ++++++++++++++++++++- Could you push these changes as a separate commit? Thanks. Florian
On 09/05/2019 14:08, Florian Weimer wrote: > * Adhemerval Zanella: > >>> support/Makefile | 3 +- >>> support/README | 5 +- >>> support/timespec-add.c | 71 ++++++++++++++++++- >>> support/timespec-sub.c | 71 ++++++++++++++++++- >>> support/timespec.c | 58 ++++++++++++++- >>> support/timespec.h | 79 ++++++++++++++++++++- > > Could you push these changes as a separate commit? Thanks. > > Florian > Ok, I will split the patch.
On Thu, 9 May 2019, Adhemerval Zanella wrote: > On 09/05/2019 14:08, Florian Weimer wrote: > > * Adhemerval Zanella: > > > >>> support/Makefile | 3 +- > >>> support/README | 5 +- > >>> support/timespec-add.c | 71 ++++++++++++++++++- > >>> support/timespec-sub.c | 71 ++++++++++++++++++- > >>> support/timespec.c | 58 ++++++++++++++- > >>> support/timespec.h | 79 ++++++++++++++++++++- > > > > Could you push these changes as a separate commit? Thanks. > > > > Florian > > > Ok, I will split the patch. These changes have broken the build for x32, with errors such as: timespec.c: In function 'test_timespec_before_impl': timespec.c:32:23: error: format '%ld' expects argument of type 'long int', but argument 4 has type '__time_t' {aka 'const long long int'} [-Werror=format=]
> Il giorno 9 mag 2019, alle ore 20:03, Joseph Myers <joseph@codesourcery.com> ha scritto: > >> On Thu, 9 May 2019, Adhemerval Zanella wrote: >> >>> On 09/05/2019 14:08, Florian Weimer wrote: >>> * Adhemerval Zanella: >>> >>>>> support/Makefile | 3 +- >>>>> support/README | 5 +- >>>>> support/timespec-add.c | 71 ++++++++++++++++++- >>>>> support/timespec-sub.c | 71 ++++++++++++++++++- >>>>> support/timespec.c | 58 ++++++++++++++- >>>>> support/timespec.h | 79 ++++++++++++++++++++- >>> >>> Could you push these changes as a separate commit? Thanks. >>> >>> Florian >>> >> Ok, I will split the patch. > > These changes have broken the build for x32, with errors such as: > > timespec.c: In function 'test_timespec_before_impl': > timespec.c:32:23: error: format '%ld' expects argument of type 'long int', but argument 4 has type '__time_t' {aka 'const long long int'} [-Werror=format=] I will check this out.
On 10/05/2019 06:42, Adhemerval Zanella wrote: > > >> Il giorno 9 mag 2019, alle ore 20:03, Joseph Myers <joseph@codesourcery.com> ha scritto: >> >>> On Thu, 9 May 2019, Adhemerval Zanella wrote: >>> >>>> On 09/05/2019 14:08, Florian Weimer wrote: >>>> * Adhemerval Zanella: >>>> >>>>>> support/Makefile | 3 +- >>>>>> support/README | 5 +- >>>>>> support/timespec-add.c | 71 ++++++++++++++++++- >>>>>> support/timespec-sub.c | 71 ++++++++++++++++++- >>>>>> support/timespec.c | 58 ++++++++++++++- >>>>>> support/timespec.h | 79 ++++++++++++++++++++- >>>> >>>> Could you push these changes as a separate commit? Thanks. >>>> >>>> Florian >>>> >>> Ok, I will split the patch. >> >> These changes have broken the build for x32, with errors such as: >> >> timespec.c: In function 'test_timespec_before_impl': >> timespec.c:32:23: error: format '%ld' expects argument of type 'long int', but argument 4 has type '__time_t' {aka 'const long long int'} [-Werror=format=] > > I will check this out. > I will check the following patch to fix it: --- diff --git a/support/timespec.c b/support/timespec.c index f1b88c1f4c..653293970a 100644 --- a/support/timespec.c +++ b/support/timespec.c @@ -18,6 +18,7 @@ #include <support/timespec.h> #include <stdio.h> +#include <stdint.h> void test_timespec_before_impl (const char *file, int line, @@ -29,12 +30,12 @@ test_timespec_before_impl (const char *file, int line, && left.tv_nsec > right.tv_nsec)) { support_record_failure (); const struct timespec diff = timespec_sub (left, right); - printf ("%s:%d: %ld.%09lds not before %ld.%09lds " - "(difference %ld.%09lds)n", + printf ("%s:%d: %jd.%09jds not before %jd.%09jds " + "(difference %jd.%09jds)n", file, line, - left.tv_sec, left.tv_nsec, - right.tv_sec, right.tv_nsec, - diff.tv_sec, diff.tv_nsec); + (intmax_t) left.tv_sec, (intmax_t) left.tv_nsec, + (intmax_t) right.tv_sec, (intmax_t) right.tv_nsec, + (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); } } @@ -48,11 +49,11 @@ test_timespec_equal_or_after_impl (const char *file, int line, && left.tv_nsec < right.tv_nsec)) { support_record_failure (); const struct timespec diff = timespec_sub (right, left); - printf ("%s:%d: %ld.%09lds not after %ld.%09lds " - "(difference %ld.%09lds)n", + printf ("%s:%d: %jd.%09jds not after %jd.%09jds " + "(difference %jd.%09jds)n", file, line, - left.tv_sec, left.tv_nsec, - right.tv_sec, right.tv_nsec, - diff.tv_sec, diff.tv_nsec); + (intmax_t) left.tv_sec, (intmax_t) left.tv_nsec, + (intmax_t) right.tv_sec, (intmax_t) right.tv_nsec, + (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); } ---
diff --git a/ChangeLog b/ChangeLog index 6008265..cd0ef83 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,23 @@ 2019-05-02 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. + + * nptl/tst-cond11.c: Use libsupport. + +2019-05-02 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/nptl/tst-cond11.c b/nptl/tst-cond11.c index 97a8bd0..3bc4ff4 100644 --- a/nptl/tst-cond11.c +++ b/nptl/tst-cond11.c @@ -21,6 +21,10 @@ #include <stdio.h> #include <time.h> #include <unistd.h> +#include <support/check.h> +#include <support/timespec.h> +#include <support/xthread.h> +#include <support/xtime.h> #if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0 @@ -34,132 +38,36 @@ run_test (clockid_t cl) printf ("clock = %d\n", (int) cl); - if (pthread_condattr_init (&condattr) != 0) - { - puts ("condattr_init failed"); - return 1; - } - - if (pthread_condattr_setclock (&condattr, cl) != 0) - { - puts ("condattr_setclock failed"); - return 1; - } + TEST_COMPARE (pthread_condattr_init (&condattr), 0); + TEST_COMPARE (pthread_condattr_setclock (&condattr, cl), 0); clockid_t cl2; - if (pthread_condattr_getclock (&condattr, &cl2) != 0) - { - puts ("condattr_getclock failed"); - return 1; - } - if (cl != cl2) - { - printf ("condattr_getclock returned wrong value: %d, expected %d\n", - (int) cl2, (int) cl); - return 1; - } - - if (pthread_cond_init (&cond, &condattr) != 0) - { - puts ("cond_init failed"); - return 1; - } - - if (pthread_condattr_destroy (&condattr) != 0) - { - puts ("condattr_destroy failed"); - return 1; - } - - if (pthread_mutexattr_init (&mutattr) != 0) - { - puts ("mutexattr_init failed"); - return 1; - } - - if (pthread_mutexattr_settype (&mutattr, PTHREAD_MUTEX_ERRORCHECK) != 0) - { - puts ("mutexattr_settype failed"); - return 1; - } - - if (pthread_mutex_init (&mut, &mutattr) != 0) - { - puts ("mutex_init failed"); - return 1; - } - - if (pthread_mutexattr_destroy (&mutattr) != 0) - { - puts ("mutexattr_destroy failed"); - return 1; - } - - if (pthread_mutex_lock (&mut) != 0) - { - puts ("mutex_lock failed"); - return 1; - } - - if (pthread_mutex_lock (&mut) != EDEADLK) - { - puts ("2nd mutex_lock did not return EDEADLK"); - return 1; - } - - struct timespec ts; - if (clock_gettime (cl, &ts) != 0) - { - puts ("clock_gettime failed"); - return 1; - } + TEST_COMPARE (pthread_condattr_getclock (&condattr, &cl2), 0); + TEST_COMPARE (cl, cl2); + + TEST_COMPARE (pthread_cond_init (&cond, &condattr), 0); + TEST_COMPARE (pthread_condattr_destroy (&condattr), 0); + + xpthread_mutexattr_init (&mutattr); + xpthread_mutexattr_settype (&mutattr, PTHREAD_MUTEX_ERRORCHECK); + xpthread_mutex_init (&mut, &mutattr); + xpthread_mutexattr_destroy (&mutattr); + + xpthread_mutex_lock (&mut); + TEST_COMPARE (pthread_mutex_lock (&mut), EDEADLK); + + struct timespec ts_timeout; + xclock_gettime (cl, &ts_timeout); /* Wait one second. */ - ++ts.tv_sec; - - int e = pthread_cond_timedwait (&cond, &mut, &ts); - if (e == 0) - { - puts ("cond_timedwait succeeded"); - return 1; - } - else if (e != ETIMEDOUT) - { - puts ("cond_timedwait did not return ETIMEDOUT"); - return 1; - } - - struct timespec ts2; - if (clock_gettime (cl, &ts2) != 0) - { - puts ("second clock_gettime failed"); - return 1; - } - - if (ts2.tv_sec < ts.tv_sec - || (ts2.tv_sec == ts.tv_sec && ts2.tv_nsec < ts.tv_nsec)) - { - puts ("timeout too short"); - return 1; - } - - if (pthread_mutex_unlock (&mut) != 0) - { - puts ("mutex_unlock failed"); - return 1; - } - - if (pthread_mutex_destroy (&mut) != 0) - { - puts ("mutex_destroy failed"); - return 1; - } - - if (pthread_cond_destroy (&cond) != 0) - { - puts ("cond_destroy failed"); - return 1; - } + ++ts_timeout.tv_sec; + + TEST_COMPARE (pthread_cond_timedwait (&cond, &mut, &ts_timeout), ETIMEDOUT); + TEST_TIMESPEC_BEFORE_NOW (ts_timeout, cl); + + xpthread_mutex_unlock (&mut); + xpthread_mutex_destroy (&mut); + TEST_COMPARE (pthread_cond_destroy (&cond), 0); return 0; } @@ -171,12 +79,11 @@ do_test (void) { #if !defined _POSIX_CLOCK_SELECTION || _POSIX_CLOCK_SELECTION == -1 - puts ("_POSIX_CLOCK_SELECTION not supported, test skipped"); - return 0; + FAIL_UNSUPPORTED ("_POSIX_CLOCK_SELECTION not supported, test skipped"); #else - int res = run_test (CLOCK_REALTIME); + run_test (CLOCK_REALTIME); # if defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0 # if _POSIX_MONOTONIC_CLOCK == 0 @@ -184,20 +91,16 @@ do_test (void) if (e < 0) puts ("CLOCK_MONOTONIC not supported"); else if (e == 0) - { - puts ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0"); - res = 1; - } + FAIL_RET ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0"); else # endif - res |= run_test (CLOCK_MONOTONIC); + run_test (CLOCK_MONOTONIC); # else puts ("_POSIX_MONOTONIC_CLOCK not defined"); # endif - return res; + return 0; #endif } -#define TEST_FUNCTION do_test () -#include "../test-skeleton.c" +#include <support/test-driver.c> diff --git a/support/Makefile b/support/Makefile index 05865fe..64044f6 100644 --- a/support/Makefile +++ b/support/Makefile @@ -71,6 +71,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..ae2c41c 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 files +contain 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..be7a590 --- /dev/null +++ b/support/timespec-add.c @@ -0,0 +1,71 @@ +/* Add two struct timespec values. + Copyright (C) 2011-2019 Free Software Foundation, Inc. + 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. + + 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/>. */ + +/* 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..bf95c7f --- /dev/null +++ b/support/timespec-sub.c @@ -0,0 +1,71 @@ +/* Subtract two struct timespec values. + Copyright (C) 2011-2019 Free Software Foundation, Inc. + 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. + + 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/>. */ + +/* 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..f1b88c1 --- /dev/null +++ b/support/timespec.c @@ -0,0 +1,58 @@ +/* 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 <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)) { + 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); + } +} + +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)) { + 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); + } +} 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 */