Message ID | xnwminoith.fsf@greed.delorie.com |
---|---|
State | New |
Headers | show |
Series | rt: more clock_nanosleep tests | expand |
On 04/10/24 14:54, DJ Delorie wrote: > > Test that clock_nanosleep rejects out of range time values. > > Test that clock_nanosleep actually sleeps for at least the > requested time relative to the requested clock. Looks good in general, some remarks below. > > diff --git a/rt/Makefile b/rt/Makefile > index 7b50c64f76..c1c7cbfe6d 100644 > --- a/rt/Makefile > +++ b/rt/Makefile > @@ -77,6 +77,7 @@ tests := tst-shm tst-timer tst-timer2 \ > tst-bz28213 \ > tst-timer3 tst-timer4 tst-timer5 \ > tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \ > + tst-cpuclock3 \ > tst-shm-cancel \ > tst-mqueue10 > tests-internal := tst-timer-sigmask > @@ -84,6 +85,7 @@ tests-internal := tst-timer-sigmask > tests-time64 := \ > tst-aio6-time64 \ > tst-cpuclock2-time64 \ > + tst-cpuclock3-time64 \ > tst-mqueue1-time64 \ > tst-mqueue2-time64 \ > tst-mqueue4-time64 \ Ok. > diff --git a/rt/tst-cpuclock3-time64.c b/rt/tst-cpuclock3-time64.c > new file mode 100644 > index 0000000000..01cb55f1d6 > --- /dev/null > +++ b/rt/tst-cpuclock3-time64.c > @@ -0,0 +1 @@ > +#include "tst-cpuclock3.c" Ok. > diff --git a/rt/tst-cpuclock3.c b/rt/tst-cpuclock3.c > new file mode 100644 > index 0000000000..59286f2dab > --- /dev/null > +++ b/rt/tst-cpuclock3.c > @@ -0,0 +1,236 @@ > +/* Test program for process CPU clocks - invalid inputs, minimum time > + Copyright (C) 2024 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 > + <https://www.gnu.org/licenses/>. */ > + > +/* This test has two primary goals - first, to validate that invalid > + inputs to clock_nanosleep are caught, and second, to validate that > + clock_nanosleep sleeps for at least the amount of time requested. > + It is assumed that the system may sleep for an arbitrary additional > + amount of time beyond the requested time. */ > + > +#include <unistd.h> > +#include <stdint.h> > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <time.h> > +#include <fcntl.h> > +#include <string.h> > +#include <errno.h> > + > +#include <support/xunistd.h> > +#include <support/check.h> > +#include <support/xthread.h> > +#include <support/timespec.h> > + > +/* This is 1 ms per test, we have 10 tests, so this file runs in on > + the order of 0.01 seconds. */ > +#define TEST_NSEC 1000000 > + > +/* Nanoseconds per second. */ > +#define NSECMAX 1000000000L > + > +static pthread_barrier_t barrier; > + > +/* This function is intended to rack up both user and system time. */ > +static void * > +chew_cpu (void *arg) > +{ > + pthread_barrier_wait (&barrier); > + > + while (1) > + { > + static volatile char buf[4096]; > + for (int i = 0; i < 100; ++i) > + for (size_t j = 0; j < sizeof buf; ++j) > + buf[j] = 0xaa; > + int nullfd = xopen ("/dev/null", O_WRONLY, 0); > + for (int i = 0; i < 100; ++i) > + for (size_t j = 0; j < sizeof buf; ++j) > + buf[j] = 0xbb; > + xwrite (nullfd, (char *) buf, sizeof buf); > + close (nullfd); > + } > + > + return NULL; > +} > + > +static void > +ptime_1 (const char *n, struct timespec t) > +{ > + /* This is only for debugging failed test cases. */ > + printf ("%12s: %lld.%09lld\n", n, (long long int) t.tv_sec, (long long int) t.tv_nsec); Line too long. > +} > +#define ptime(t) ptime_1 (#t, t) > + > +static void > +test_interval_1 (const char *n_clock, clockid_t t_clock) > +{ > + struct timespec me_before, me_after, quantum, me_sleep, me_slept; > + long long int slept, min_slept; > + > + /* Arbitrary to ensure our time period is sufficiently bigger than > + the time step. */ > + TEST_VERIFY (clock_getres (t_clock, &quantum) == 0); > + printf("Clock quantum: %lld ns, test time: %lld ns\n", > + (long long int) quantum.tv_nsec, (long long int) TEST_NSEC); > + TEST_VERIFY (quantum.tv_nsec <= TEST_NSEC / 10); > + > + min_slept = TEST_NSEC; > + > + me_sleep = make_timespec (0, min_slept); > + > + printf ("test clock %s for %lld.%09lld sec relative\n", > + n_clock, (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); Ditto. > + > + TEST_COMPARE (clock_gettime (t_clock, &me_before), 0); > + TEST_COMPARE (clock_nanosleep (t_clock, 0, &me_sleep, NULL), 0); > + TEST_COMPARE (clock_gettime (t_clock, &me_after), 0); > + > + me_slept = timespec_sub (me_after, me_before); > + slept = support_timespec_ns (me_slept); > + > + ptime (me_before); > + ptime (me_after); > + ptime (me_sleep); > + ptime (me_slept); > + printf ("test slept %lld nsec >= asked for %lld ?\n", slept, min_slept); > + > + /* This is the important part - verify that the time slept is at > + least as much as the time requested. */ > + TEST_VERIFY (slept >= min_slept); > +} > + > +static void > +test_abs_1 (const char *n_clock, clockid_t t_clock) > +{ > + struct timespec me_before, me_after, quantum, me_sleep; > + > + /* Arbitrary to ensure our time period is sufficiently bigger than > + the time step. */ > + TEST_VERIFY (clock_getres (t_clock, &quantum) == 0); > + printf("Clock quantum: %lld ns, test time: %lld ns\n", > + (long long int) quantum.tv_nsec, (long long int) TEST_NSEC); > + TEST_VERIFY (quantum.tv_nsec <= TEST_NSEC / 10); > + > + me_sleep = make_timespec (0, TEST_NSEC); > + > + printf ("test clock %s for %lld.%09lld sec absolute\n", > + n_clock, (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); > + > + TEST_COMPARE (clock_gettime (t_clock, &me_before), 0); > + me_sleep = timespec_add (me_sleep, me_before); > + TEST_COMPARE (clock_nanosleep (t_clock, TIMER_ABSTIME, &me_sleep, NULL), 0); > + TEST_COMPARE (clock_gettime (t_clock, &me_after), 0); > + > + ptime (me_before); > + ptime (me_sleep); > + ptime (me_after); > + > + printf("test slept until %lld.%09lld after requested %lld.%09lld ?\n", > + (long long int) me_after.tv_sec, (long long int) me_after.tv_nsec, > + (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); > + > + /* This is the important part - verify that the time slept is at > + least as much as the time requested. */ > + TEST_TIMESPEC_EQUAL_OR_AFTER (me_after, me_sleep); > +} > + > +static void > +test_invalids_1 (const char *the_clock_name, int the_clock, > + const char *flags_name, int flags) > +{ > + struct timespec me_before; > + > + /* Note: do not use make_timespec() in case that function tries to > + normalize the fields. */ > + > + printf ("%s: %s: test tv 0, 0\n", the_clock_name, flags_name); > + me_before.tv_sec = 0; > + me_before.tv_nsec = 0; > + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), 0); > + Spurious double space here. > + printf ("%s: %s: test tv -1, 0\n", the_clock_name, flags_name); > + me_before.tv_sec = -1; > + me_before.tv_nsec = 0; > + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); > + > + printf ("%s: %s: test tv 0, -1\n", the_clock_name, flags_name); > + me_before.tv_sec = 0; > + me_before.tv_nsec = -1; > + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); > + > + printf ("%s: %s: test tv -1, -1\n", the_clock_name, flags_name); > + me_before.tv_sec = -1; > + me_before.tv_nsec = -1; > + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); > + > + printf ("%s: %s: test tv 0, MAX\n", the_clock_name, flags_name); > + me_before.tv_sec = 0; > + me_before.tv_nsec = NSECMAX; > + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); > +} > + > +static int > +do_test (void) > +{ > + pthread_t th; > + > + pthread_barrier_init (&barrier, NULL, 2); > + > + /* Test for proper error detection. */ > + > +#define test_invalids(c, f) test_invalids_1 (#c, c, #f, f) > + test_invalids (CLOCK_REALTIME, 0); > + test_invalids (CLOCK_TAI, 0); > + test_invalids (CLOCK_MONOTONIC, 0); > + test_invalids (CLOCK_BOOTTIME, 0); > + test_invalids (CLOCK_PROCESS_CPUTIME_ID, 0); > + test_invalids (CLOCK_REALTIME, TIMER_ABSTIME); > + test_invalids (CLOCK_TAI, TIMER_ABSTIME); > + test_invalids (CLOCK_MONOTONIC, TIMER_ABSTIME); > + test_invalids (CLOCK_BOOTTIME, TIMER_ABSTIME); > + test_invalids (CLOCK_PROCESS_CPUTIME_ID, TIMER_ABSTIME); Both CLOCK_TAI and CLOCK_BOOTTIME are Linux specific, so we will need to check if they exist before test them. > + > + /* Test for various clocks "working". */ > + > +#define test_interval(c) test_interval_1 (#c, c) > + test_interval (CLOCK_REALTIME); > + test_interval (CLOCK_TAI); > + test_interval (CLOCK_MONOTONIC); > + test_interval (CLOCK_BOOTTIME); > + > + th = xpthread_create (NULL, chew_cpu, NULL); > + xpthread_barrier_wait (&barrier); > + test_interval (CLOCK_PROCESS_CPUTIME_ID); > + xpthread_cancel (th); > + > +#define test_abs(c) test_abs_1 (#c, c) > + test_abs (CLOCK_REALTIME); > + test_abs (CLOCK_TAI); > + test_abs (CLOCK_MONOTONIC); > + test_abs (CLOCK_BOOTTIME); > + > + th = xpthread_create (NULL, chew_cpu, NULL); > + xpthread_barrier_wait (&barrier); > + test_abs (CLOCK_PROCESS_CPUTIME_ID); > + xpthread_cancel (th); > + > + return 0; > +} > + > +#include <support/test-driver.c> >
diff --git a/rt/Makefile b/rt/Makefile index 7b50c64f76..c1c7cbfe6d 100644 --- a/rt/Makefile +++ b/rt/Makefile @@ -77,6 +77,7 @@ tests := tst-shm tst-timer tst-timer2 \ tst-bz28213 \ tst-timer3 tst-timer4 tst-timer5 \ tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \ + tst-cpuclock3 \ tst-shm-cancel \ tst-mqueue10 tests-internal := tst-timer-sigmask @@ -84,6 +85,7 @@ tests-internal := tst-timer-sigmask tests-time64 := \ tst-aio6-time64 \ tst-cpuclock2-time64 \ + tst-cpuclock3-time64 \ tst-mqueue1-time64 \ tst-mqueue2-time64 \ tst-mqueue4-time64 \ diff --git a/rt/tst-cpuclock3-time64.c b/rt/tst-cpuclock3-time64.c new file mode 100644 index 0000000000..01cb55f1d6 --- /dev/null +++ b/rt/tst-cpuclock3-time64.c @@ -0,0 +1 @@ +#include "tst-cpuclock3.c" diff --git a/rt/tst-cpuclock3.c b/rt/tst-cpuclock3.c new file mode 100644 index 0000000000..59286f2dab --- /dev/null +++ b/rt/tst-cpuclock3.c @@ -0,0 +1,236 @@ +/* Test program for process CPU clocks - invalid inputs, minimum time + Copyright (C) 2024 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 + <https://www.gnu.org/licenses/>. */ + +/* This test has two primary goals - first, to validate that invalid + inputs to clock_nanosleep are caught, and second, to validate that + clock_nanosleep sleeps for at least the amount of time requested. + It is assumed that the system may sleep for an arbitrary additional + amount of time beyond the requested time. */ + +#include <unistd.h> +#include <stdint.h> + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> + +#include <support/xunistd.h> +#include <support/check.h> +#include <support/xthread.h> +#include <support/timespec.h> + +/* This is 1 ms per test, we have 10 tests, so this file runs in on + the order of 0.01 seconds. */ +#define TEST_NSEC 1000000 + +/* Nanoseconds per second. */ +#define NSECMAX 1000000000L + +static pthread_barrier_t barrier; + +/* This function is intended to rack up both user and system time. */ +static void * +chew_cpu (void *arg) +{ + pthread_barrier_wait (&barrier); + + while (1) + { + static volatile char buf[4096]; + for (int i = 0; i < 100; ++i) + for (size_t j = 0; j < sizeof buf; ++j) + buf[j] = 0xaa; + int nullfd = xopen ("/dev/null", O_WRONLY, 0); + for (int i = 0; i < 100; ++i) + for (size_t j = 0; j < sizeof buf; ++j) + buf[j] = 0xbb; + xwrite (nullfd, (char *) buf, sizeof buf); + close (nullfd); + } + + return NULL; +} + +static void +ptime_1 (const char *n, struct timespec t) +{ + /* This is only for debugging failed test cases. */ + printf ("%12s: %lld.%09lld\n", n, (long long int) t.tv_sec, (long long int) t.tv_nsec); +} +#define ptime(t) ptime_1 (#t, t) + +static void +test_interval_1 (const char *n_clock, clockid_t t_clock) +{ + struct timespec me_before, me_after, quantum, me_sleep, me_slept; + long long int slept, min_slept; + + /* Arbitrary to ensure our time period is sufficiently bigger than + the time step. */ + TEST_VERIFY (clock_getres (t_clock, &quantum) == 0); + printf("Clock quantum: %lld ns, test time: %lld ns\n", + (long long int) quantum.tv_nsec, (long long int) TEST_NSEC); + TEST_VERIFY (quantum.tv_nsec <= TEST_NSEC / 10); + + min_slept = TEST_NSEC; + + me_sleep = make_timespec (0, min_slept); + + printf ("test clock %s for %lld.%09lld sec relative\n", + n_clock, (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); + + TEST_COMPARE (clock_gettime (t_clock, &me_before), 0); + TEST_COMPARE (clock_nanosleep (t_clock, 0, &me_sleep, NULL), 0); + TEST_COMPARE (clock_gettime (t_clock, &me_after), 0); + + me_slept = timespec_sub (me_after, me_before); + slept = support_timespec_ns (me_slept); + + ptime (me_before); + ptime (me_after); + ptime (me_sleep); + ptime (me_slept); + printf ("test slept %lld nsec >= asked for %lld ?\n", slept, min_slept); + + /* This is the important part - verify that the time slept is at + least as much as the time requested. */ + TEST_VERIFY (slept >= min_slept); +} + +static void +test_abs_1 (const char *n_clock, clockid_t t_clock) +{ + struct timespec me_before, me_after, quantum, me_sleep; + + /* Arbitrary to ensure our time period is sufficiently bigger than + the time step. */ + TEST_VERIFY (clock_getres (t_clock, &quantum) == 0); + printf("Clock quantum: %lld ns, test time: %lld ns\n", + (long long int) quantum.tv_nsec, (long long int) TEST_NSEC); + TEST_VERIFY (quantum.tv_nsec <= TEST_NSEC / 10); + + me_sleep = make_timespec (0, TEST_NSEC); + + printf ("test clock %s for %lld.%09lld sec absolute\n", + n_clock, (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); + + TEST_COMPARE (clock_gettime (t_clock, &me_before), 0); + me_sleep = timespec_add (me_sleep, me_before); + TEST_COMPARE (clock_nanosleep (t_clock, TIMER_ABSTIME, &me_sleep, NULL), 0); + TEST_COMPARE (clock_gettime (t_clock, &me_after), 0); + + ptime (me_before); + ptime (me_sleep); + ptime (me_after); + + printf("test slept until %lld.%09lld after requested %lld.%09lld ?\n", + (long long int) me_after.tv_sec, (long long int) me_after.tv_nsec, + (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); + + /* This is the important part - verify that the time slept is at + least as much as the time requested. */ + TEST_TIMESPEC_EQUAL_OR_AFTER (me_after, me_sleep); +} + +static void +test_invalids_1 (const char *the_clock_name, int the_clock, + const char *flags_name, int flags) +{ + struct timespec me_before; + + /* Note: do not use make_timespec() in case that function tries to + normalize the fields. */ + + printf ("%s: %s: test tv 0, 0\n", the_clock_name, flags_name); + me_before.tv_sec = 0; + me_before.tv_nsec = 0; + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), 0); + + printf ("%s: %s: test tv -1, 0\n", the_clock_name, flags_name); + me_before.tv_sec = -1; + me_before.tv_nsec = 0; + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); + + printf ("%s: %s: test tv 0, -1\n", the_clock_name, flags_name); + me_before.tv_sec = 0; + me_before.tv_nsec = -1; + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); + + printf ("%s: %s: test tv -1, -1\n", the_clock_name, flags_name); + me_before.tv_sec = -1; + me_before.tv_nsec = -1; + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); + + printf ("%s: %s: test tv 0, MAX\n", the_clock_name, flags_name); + me_before.tv_sec = 0; + me_before.tv_nsec = NSECMAX; + TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); +} + +static int +do_test (void) +{ + pthread_t th; + + pthread_barrier_init (&barrier, NULL, 2); + + /* Test for proper error detection. */ + +#define test_invalids(c, f) test_invalids_1 (#c, c, #f, f) + test_invalids (CLOCK_REALTIME, 0); + test_invalids (CLOCK_TAI, 0); + test_invalids (CLOCK_MONOTONIC, 0); + test_invalids (CLOCK_BOOTTIME, 0); + test_invalids (CLOCK_PROCESS_CPUTIME_ID, 0); + test_invalids (CLOCK_REALTIME, TIMER_ABSTIME); + test_invalids (CLOCK_TAI, TIMER_ABSTIME); + test_invalids (CLOCK_MONOTONIC, TIMER_ABSTIME); + test_invalids (CLOCK_BOOTTIME, TIMER_ABSTIME); + test_invalids (CLOCK_PROCESS_CPUTIME_ID, TIMER_ABSTIME); + + /* Test for various clocks "working". */ + +#define test_interval(c) test_interval_1 (#c, c) + test_interval (CLOCK_REALTIME); + test_interval (CLOCK_TAI); + test_interval (CLOCK_MONOTONIC); + test_interval (CLOCK_BOOTTIME); + + th = xpthread_create (NULL, chew_cpu, NULL); + xpthread_barrier_wait (&barrier); + test_interval (CLOCK_PROCESS_CPUTIME_ID); + xpthread_cancel (th); + +#define test_abs(c) test_abs_1 (#c, c) + test_abs (CLOCK_REALTIME); + test_abs (CLOCK_TAI); + test_abs (CLOCK_MONOTONIC); + test_abs (CLOCK_BOOTTIME); + + th = xpthread_create (NULL, chew_cpu, NULL); + xpthread_barrier_wait (&barrier); + test_abs (CLOCK_PROCESS_CPUTIME_ID); + xpthread_cancel (th); + + return 0; +} + +#include <support/test-driver.c>