diff mbox series

rt: more clock_nanosleep tests

Message ID xnwminoith.fsf@greed.delorie.com
State New
Headers show
Series rt: more clock_nanosleep tests | expand

Commit Message

DJ Delorie Oct. 4, 2024, 5:54 p.m. UTC
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.

Comments

Adhemerval Zanella Netto Oct. 7, 2024, 7:41 p.m. UTC | #1
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 mbox series

Patch

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>