summaryrefslogtreecommitdiff
path: root/rt
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>2005-04-27 08:03:47 +0000
committerRoland McGrath <roland@gnu.org>2005-04-27 08:03:47 +0000
commit84060bad8250d366c925f66bfba03efce9f4265c (patch)
tree9a7e4f3e4bb5d0e6b3da14712712ec38f79e4a95 /rt
parent2f4f3bd4a9ad805383b278e5b975971ca15c7a77 (diff)
downloadglibc-84060bad8250d366c925f66bfba03efce9f4265c.tar.gz
* rt/tst-cpuclock1.c: New file.
* rt/tst-cpuclock2.c: New file. * rt/tst-cputimer1.c: New file. * rt/tst-cputimer2.c: New file. * rt/tst-cputimer3.c: New file. * rt/Makefile (tests): Add them. * sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h: New file. * sysdeps/unix/sysv/linux/clock_getcpuclockid.c: New file. * sysdeps/unix/sysv/linux/ia64/clock_getcpuclockid.c (HAS_CPUCLOCK): New macro. (clock_getcpuclockid): Function removed. #include the new linux file to define it instead. * sysdeps/unix/clock_gettime.c [HP_TIMING_AVAIL] (hp_timing_gettime): New function, broken out of ... (clock_gettime) [HP_TIMING_AVAIL]: ... here. Call it. (realtime_gettime): New function, broken out of ... (clock_gettime) [! HANDLED_REALTIME]: ... here. Call it. (clock_gettime) [SYSDEP_GETTIME_CPU]: Use new macro in default case. * sysdeps/unix/sysv/linux/clock_gettime.c (SYSCALL_GETTIME): New macro. (SYSDEP_GETTIME_CPUTIME): New macro. (SYSDEP_GETTIME): Use both. [! __ASSUME_POSIX_TIMERS] (maybe_syscall_gettime): New function, broken out of ... (SYSDEP_GETTIME): ... here. Use it. [__NR_clock_gettime] (HANDLED_CPUTIME): Define it. (SYSDEP_GETTIME_CPUTIME): New macro. Handle CPU timers by trying kernel support and falling back to hp-timing code. * sysdeps/posix/clock_getres.c [HP_TIMING_AVAIL] (hp_timing_getres): New function, broken out of ... (clock_getres) [HP_TIMING_AVAIL]: ... here. Call it. (realtime_getres): New function, broken out of ... (clock_getres) [! HANDLED_REALTIME]: ... here. Call it. (clock_getres) [SYSDEP_GETRES_CPU]: Use new macro in default case. * sysdeps/unix/sysv/linux/clock_getres.c (SYSCALL_GETRES): New macro. (SYSDEP_GETRES_CPUTIME): New macro. (SYSDEP_GETRES): Use both. [! __ASSUME_POSIX_TIMERS] (maybe_syscall_getres): New function, broken out of ... (SYSDEP_GETRES): ... here. Use it. [__NR_clock_getres] (HANDLED_CPUTIME): Define it. (SYSDEP_GETRES_CPUTIME): New macro. Handle CPU timers by trying kernel support and falling back to hp-timing code. * sysdeps/unix/sysv/linux/clock_nanosleep.c: Handle CLOCK_PROCESS_CPUTIME_ID and CLOCK_PROCESS_THREAD_ID specially, translating to the kernel clockid_t for our own process/thread clock.
Diffstat (limited to 'rt')
-rw-r--r--rt/Makefile4
-rw-r--r--rt/tst-cpuclock1.c307
-rw-r--r--rt/tst-cpuclock2.c332
-rw-r--r--rt/tst-cputimer1.c68
-rw-r--r--rt/tst-cputimer2.c83
-rw-r--r--rt/tst-cputimer3.c130
6 files changed, 923 insertions, 1 deletions
diff --git a/rt/Makefile b/rt/Makefile
index 407cc99897..cc01cb95c9 100644
--- a/rt/Makefile
+++ b/rt/Makefile
@@ -45,7 +45,9 @@ tests := tst-shm tst-clock tst-clock_nanosleep tst-timer tst-timer2 \
tst-aio tst-aio64 tst-aio2 tst-aio3 tst-aio4 tst-aio5 tst-aio6 \
tst-aio7 tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4 \
tst-mqueue5 tst-mqueue6 tst-mqueue7 tst-mqueue8 tst-mqueue9 \
- tst-timer3 tst-timer4 tst-timer5
+ tst-timer3 tst-timer4 tst-timer5 \
+ tst-cpuclock1 tst-cpuclock2 \
+ tst-cputimer1 tst-cputimer2 tst-cputimer3
extra-libs := librt
extra-libs-others := $(extra-libs)
diff --git a/rt/tst-cpuclock1.c b/rt/tst-cpuclock1.c
new file mode 100644
index 0000000000..024df63141
--- /dev/null
+++ b/rt/tst-cpuclock1.c
@@ -0,0 +1,307 @@
+/* Test program for process CPU clocks.
+ Copyright (C) 2004 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, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+/* This function is intended to rack up both user and system time. */
+static void
+chew_cpu (void)
+{
+ 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 = open ("/dev/null", O_WRONLY);
+ for (int i = 0; i < 100; ++i)
+ for (size_t j = 0; j < sizeof buf; ++j)
+ buf[j] = 0xbb;
+ write (nullfd, (char *) buf, sizeof buf);
+ close (nullfd);
+ if (getppid () == 1)
+ _exit (2);
+ }
+}
+
+static int
+do_test (void)
+{
+ int result = 0;
+ clockid_t cl;
+ int e;
+ pid_t dead_child, child;
+
+ /* Fork a child and let it die, to give us a PID known not be valid
+ (assuming PIDs don't wrap around during the test). */
+ {
+ dead_child = fork ();
+ if (dead_child == 0)
+ _exit (0);
+ if (dead_child < 0)
+ {
+ perror ("fork");
+ return 1;
+ }
+ int x;
+ if (wait (&x) != dead_child)
+ {
+ perror ("wait");
+ return 2;
+ }
+ }
+
+ /* POSIX says we should get ESRCH for this. */
+ e = clock_getcpuclockid (dead_child, &cl);
+ if (e != ENOSYS && e != ESRCH && e != EPERM)
+ {
+ printf ("clock_getcpuclockid on dead PID %d => %s\n",
+ dead_child, strerror (e));
+ result = 1;
+ }
+
+ /* Now give us a live child eating up CPU time. */
+ child = fork ();
+ if (child == 0)
+ {
+ chew_cpu ();
+ _exit (1);
+ }
+ if (child < 0)
+ {
+ perror ("fork");
+ return 1;
+ }
+
+ e = clock_getcpuclockid (child, &cl);
+ if (e == EPERM)
+ {
+ puts ("clock_getcpuclockid does not support other processes");
+ goto done;
+ }
+ if (e != 0)
+ {
+ printf ("clock_getcpuclockid on live PID %d => %s\n",
+ child, strerror (e));
+ result = 1;
+ goto done;
+ }
+
+ const clockid_t child_clock = cl;
+ struct timespec res;
+ if (clock_getres (child_clock, &res) < 0)
+ {
+ printf ("clock_getres on live PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ result = 1;
+ goto done;
+ }
+ printf ("live PID %d clock %lx resolution %lu.%.9lu\n",
+ child, (unsigned long int) child_clock, res.tv_sec, res.tv_nsec);
+
+ struct timespec before, after;
+ if (clock_gettime (child_clock, &before) < 0)
+ {
+ printf ("clock_gettime on live PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ result = 1;
+ goto done;
+ }
+ printf ("live PID %d before sleep => %lu.%.9lu\n",
+ child, before.tv_sec, before.tv_nsec);
+
+ struct timespec sleeptime = { .tv_nsec = 500000000 };
+ nanosleep (&sleeptime, NULL);
+
+ if (clock_gettime (child_clock, &after) < 0)
+ {
+ printf ("clock_gettime on live PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ result = 1;
+ goto done;
+ }
+ printf ("live PID %d after sleep => %lu.%.9lu\n",
+ child, after.tv_sec, after.tv_nsec);
+
+ struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec,
+ .tv_nsec = after.tv_nsec - before.tv_nsec };
+ if (diff.tv_nsec < 0)
+ {
+ --diff.tv_sec;
+ diff.tv_nsec += 1000000000;
+ }
+ if (diff.tv_sec != 0
+ || diff.tv_nsec > 600000000
+ || diff.tv_nsec < 100000000)
+ {
+ printf ("before - after %lu.%.9lu outside reasonable range\n",
+ diff.tv_sec, diff.tv_nsec);
+ result = 1;
+ }
+
+ sleeptime.tv_nsec = 100000000;
+ e = clock_nanosleep (child_clock, 0, &sleeptime, NULL);
+ if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
+ {
+ printf ("clock_nanosleep not supported for other process clock: %s\n",
+ strerror (e));
+ }
+ else if (e != 0)
+ {
+ printf ("clock_nanosleep on other process clock: %s\n", strerror (e));
+ result = 1;
+ }
+ else
+ {
+ struct timespec afterns;
+ if (clock_gettime (child_clock, &afterns) < 0)
+ {
+ printf ("clock_gettime on live PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ result = 1;
+ }
+ else
+ {
+ struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec,
+ .tv_nsec = afterns.tv_nsec - after.tv_nsec };
+ if (d.tv_nsec < 0)
+ {
+ --d.tv_sec;
+ d.tv_nsec += 1000000000;
+ }
+ if (d.tv_sec > 0
+ || d.tv_nsec < sleeptime.tv_nsec
+ || d.tv_nsec > sleeptime.tv_nsec * 2)
+ {
+ printf ("nanosleep time %lu.%.9lu outside reasonable range\n",
+ d.tv_sec, d.tv_nsec);
+ result = 1;
+ }
+ }
+ }
+
+ if (kill (child, SIGKILL) != 0)
+ {
+ perror ("kill");
+ result = 2;
+ goto done;
+ }
+
+ /* Wait long enough to let the child finish dying. */
+
+ sleeptime.tv_nsec = 200000000;
+ nanosleep (&sleeptime, NULL);
+
+ struct timespec dead;
+ if (clock_gettime (child_clock, &dead) < 0)
+ {
+ printf ("clock_gettime on dead PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ result = 1;
+ goto done;
+ }
+ printf ("dead PID %d => %lu.%.9lu\n",
+ child, dead.tv_sec, dead.tv_nsec);
+
+ diff.tv_sec = dead.tv_sec - after.tv_sec;
+ diff.tv_nsec = dead.tv_nsec - after.tv_nsec;
+ if (diff.tv_nsec < 0)
+ {
+ --diff.tv_sec;
+ diff.tv_nsec += 1000000000;
+ }
+ if (diff.tv_sec != 0 || diff.tv_nsec > 200000000)
+ {
+ printf ("dead - after %lu.%.9lu outside reasonable range\n",
+ diff.tv_sec, diff.tv_nsec);
+ result = 1;
+ }
+
+ /* Now reap the child and verify that its clock is no longer valid. */
+ {
+ int x;
+ if (waitpid (child, &x, 0) != child)
+ {
+ perror ("waitpid");
+ result = 1;
+ }
+ }
+
+ if (clock_gettime (child_clock, &dead) == 0)
+ {
+ printf ("clock_gettime on reaped PID %d clock %lx => %lu%.9lu\n",
+ child, (unsigned long int) child_clock,
+ dead.tv_sec, dead.tv_nsec);
+ result = 1;
+ }
+ else
+ {
+ if (errno != EINVAL)
+ result = 1;
+ printf ("clock_gettime on reaped PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ }
+
+ if (clock_getres (child_clock, &dead) == 0)
+ {
+ printf ("clock_getres on reaped PID %d clock %lx => %lu%.9lu\n",
+ child, (unsigned long int) child_clock,
+ dead.tv_sec, dead.tv_nsec);
+ result = 1;
+ }
+ else
+ {
+ if (errno != EINVAL)
+ result = 1;
+ printf ("clock_getres on reaped PID %d clock %lx => %s\n",
+ child, (unsigned long int) child_clock, strerror (errno));
+ }
+
+ return result;
+
+ done:
+ {
+ if (kill (child, SIGKILL) != 0 && errno != ESRCH)
+ {
+ perror ("kill");
+ return 2;
+ }
+ int x;
+ if (waitpid (child, &x, 0) != child && errno != ECHILD)
+ {
+ perror ("waitpid");
+ return 2;
+ }
+ }
+
+ return result;
+}
+
+
+#define TIMEOUT 5
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/rt/tst-cpuclock2.c b/rt/tst-cpuclock2.c
new file mode 100644
index 0000000000..d1621f3d01
--- /dev/null
+++ b/rt/tst-cpuclock2.c
@@ -0,0 +1,332 @@
+/* Test program for process and thread CPU clocks.
+ Copyright (C) 2005 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, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <unistd.h>
+
+#if (_POSIX_THREADS - 0) <= 0
+
+# define TEST_FUNCTION 0
+
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+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 = open ("/dev/null", O_WRONLY);
+ for (int i = 0; i < 100; ++i)
+ for (size_t j = 0; j < sizeof buf; ++j)
+ buf[j] = 0xbb;
+ write (nullfd, (char *) buf, sizeof buf);
+ close (nullfd);
+ }
+
+ return NULL;
+}
+
+static unsigned long long int
+tsdiff (const struct timespec *before, const struct timespec *after)
+{
+ struct timespec diff = { .tv_sec = after->tv_sec - before->tv_sec,
+ .tv_nsec = after->tv_nsec - before->tv_nsec };
+ while (diff.tv_nsec < 0)
+ {
+ --diff.tv_sec;
+ diff.tv_nsec += 1000000000;
+ }
+ return diff.tv_sec * 1000000000ULL + diff.tv_nsec;
+}
+
+static unsigned long long int
+test_nanosleep (clockid_t clock, const char *which,
+ const struct timespec *before, int *bad)
+{
+ const struct timespec sleeptime = { .tv_nsec = 100000000 };
+ int e = clock_nanosleep (clock, 0, &sleeptime, NULL);
+ if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
+ {
+ printf ("clock_nanosleep not supported for %s CPU clock: %s\n",
+ which, strerror (e));
+ return 0;
+ }
+ if (e != 0)
+ {
+ printf ("clock_nanosleep on %s CPU clock: %s\n", which, strerror (e));
+ *bad = 1;
+ return 0;
+ }
+
+ struct timespec after;
+ if (clock_gettime (clock, &after) < 0)
+ {
+ printf ("clock_gettime on %s CPU clock %lx => %s\n",
+ which, (unsigned long int) clock, strerror (errno));
+ *bad = 1;
+ return 0;
+ }
+
+ unsigned long long int diff = tsdiff (before, &after);
+ if (diff < sleeptime.tv_nsec || diff > sleeptime.tv_nsec * 2)
+ {
+ printf ("clock_nanosleep on %s slept %llu (outside reasonable range)\n",
+ which, diff);
+ *bad = 1;
+ return diff;
+ }
+
+ struct timespec sleeptimeabs = sleeptime;
+ sleeptimeabs.tv_sec += after.tv_sec;
+ sleeptimeabs.tv_nsec += after.tv_nsec;
+ while (sleeptimeabs.tv_nsec > 1000000000)
+ {
+ ++sleeptimeabs.tv_sec;
+ sleeptimeabs.tv_nsec -= 1000000000;
+ }
+ e = clock_nanosleep (clock, TIMER_ABSTIME, &sleeptimeabs, NULL);
+ if (e != 0)
+ {
+ printf ("absolute clock_nanosleep on %s CPU clock: %s\n",
+ which, strerror (e));
+ *bad = 1;
+ return diff;
+ }
+
+ struct timespec afterabs;
+ if (clock_gettime (clock, &afterabs) < 0)
+ {
+ printf ("clock_gettime on %s CPU clock %lx => %s\n",
+ which, (unsigned long int) clock, strerror (errno));
+ *bad = 1;
+ return diff;
+ }
+
+ unsigned long long int sleepdiff = tsdiff (&sleeptimeabs, &afterabs);
+ if (sleepdiff > sleeptime.tv_nsec)
+ {
+ printf ("\
+absolute clock_nanosleep on %s %llu past target (outside reasonable range)\n",
+ which, sleepdiff);
+ *bad = 1;
+ }
+
+ unsigned long long int diffabs = tsdiff (&after, &afterabs);
+ if (diffabs < sleeptime.tv_nsec || diffabs > sleeptime.tv_nsec * 2)
+ {
+ printf ("\
+absolute clock_nanosleep on %s slept %llu (outside reasonable range)\n",
+ which, diffabs);
+ *bad = 1;
+ }
+
+ return diff + diffabs;
+}
+
+
+
+static int
+do_test (void)
+{
+ int result = 0;
+ clockid_t process_clock, th_clock, my_thread_clock;
+ int e;
+ pthread_t th;
+
+ e = clock_getcpuclockid (0, &process_clock);
+ if (e != 0)
+ {
+ printf ("clock_getcpuclockid on self => %s\n", strerror (e));
+ return 1;
+ }
+
+ e = pthread_getcpuclockid (pthread_self (), &my_thread_clock);
+ if (e != 0)
+ {
+ printf ("pthread_getcpuclockid on self => %s\n", strerror (e));
+ return 1;
+ }
+
+ /* This is a kludge. This test fails if the semantics of thread and
+ process clocks are wrong. The old code using hp-timing without kernel
+ support has bogus semantics if there are context switches. We don't
+ fail to report failure when the proper functionality is not available
+ in the kernel. It so happens that Linux kernels without correct CPU
+ clock support also lack CPU timer support, so we use use that to guess
+ that we are using the bogus code and not test it. */
+ timer_t t;
+ if (timer_create (my_thread_clock, NULL, &t) != 0)
+ {
+ printf ("timer_create: %m\n");
+ puts ("No support for CPU clocks with good semantics, skipping test");
+ return 0;
+ }
+ timer_delete (t);
+
+
+ pthread_barrier_init (&barrier, NULL, 2);
+
+ e = pthread_create (&th, NULL, chew_cpu, NULL);
+ if (e != 0)
+ {
+ printf ("pthread_create: %s\n", strerror (e));
+ return 1;
+ }
+
+ e = pthread_getcpuclockid (th, &th_clock);
+ if (e == ENOENT || e == ENOSYS || e == ENOTSUP)
+ {
+ puts ("pthread_getcpuclockid does not support other threads");
+ return 1;
+ }
+
+ pthread_barrier_wait (&barrier);
+
+ struct timespec res;
+ if (clock_getres (th_clock, &res) < 0)
+ {
+ printf ("clock_getres on thread clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ result = 1;
+ return 1;
+ }
+ printf ("live thread clock %lx resolution %lu.%.9lu\n",
+ (unsigned long int) th_clock, res.tv_sec, res.tv_nsec);
+
+ struct timespec process_before, process_after;
+ if (clock_gettime (process_clock, &process_before) < 0)
+ {
+ printf ("clock_gettime on process clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ return 1;
+ }
+
+ struct timespec before, after;
+ if (clock_gettime (th_clock, &before) < 0)
+ {
+ printf ("clock_gettime on live thread clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ return 1;
+ }
+ printf ("live thread before sleep => %lu.%.9lu\n",
+ before.tv_sec, before.tv_nsec);
+
+ struct timespec me_before, me_after;
+ if (clock_gettime (my_thread_clock, &me_before) < 0)
+ {
+ printf ("clock_gettime on live thread clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ return 1;
+ }
+ printf ("self thread before sleep => %lu.%.9lu\n",
+ me_before.tv_sec, me_before.tv_nsec);
+
+ struct timespec sleeptime = { .tv_nsec = 500000000 };
+ nanosleep (&sleeptime, NULL);
+
+ if (clock_gettime (th_clock, &after) < 0)
+ {
+ printf ("clock_gettime on live thread clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ return 1;
+ }
+ printf ("live thread after sleep => %lu.%.9lu\n",
+ after.tv_sec, after.tv_nsec);
+
+ if (clock_gettime (process_clock, &process_after) < 0)
+ {
+ printf ("clock_gettime on process clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ return 1;
+ }
+
+ if (clock_gettime (my_thread_clock, &me_after) < 0)
+ {
+ printf ("clock_gettime on live thread clock %lx => %s\n",
+ (unsigned long int) th_clock, strerror (errno));
+ return 1;
+ }
+ printf ("self thread after sleep => %lu.%.9lu\n",
+ me_after.tv_sec, me_after.tv_nsec);
+
+ unsigned long long int th_diff = tsdiff (&before, &after);
+ unsigned long long int pdiff = tsdiff (&process_before, &process_after);
+ unsigned long long int my_diff = tsdiff (&me_before, &me_after);
+
+ if (th_diff < 100000000 || th_diff > 600000000)
+ {
+ printf ("thread before - after %llu outside reasonable range\n",
+ th_diff);
+ result = 1;
+ }
+
+ if (my_diff > 100000000)
+ {
+ printf ("self thread before - after %llu outside reasonable range\n",
+ my_diff);
+ result = 1;
+ }
+
+ if (pdiff < th_diff)
+ {
+ printf ("process before - after %llu outside reasonable range (%llu)\n",
+ pdiff, th_diff);
+ result = 1;
+ }
+
+ process_after.tv_nsec += test_nanosleep (th_clock, "thread",
+ &after, &result);
+ process_after.tv_nsec += test_nanosleep (process_clock, "process",
+ &process_after, &result);
+ test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
+ "PROCESS_CPUTIME_ID", &process_after, &result);
+
+ pthread_cancel (th);
+
+ e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, 0, &sleeptime, NULL);
+ if (e != EINVAL)
+ {
+ printf ("clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n",
+ strerror (e));
+ result = 1;
+ }
+
+ return result;
+}
+# define TIMEOUT 8
+# define TEST_FUNCTION do_test ()
+#endif
+
+#include "../test-skeleton.c"
diff --git a/rt/tst-cputimer1.c b/rt/tst-cputimer1.c
new file mode 100644
index 0000000000..8f5dd76cf2
--- /dev/null
+++ b/rt/tst-cputimer1.c
@@ -0,0 +1,68 @@
+/* Tests for POSIX timer implementation using process CPU clock. */
+
+#include <unistd.h>
+
+#if _POSIX_THREADS && defined _POSIX_CPUTIME
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pthread.h>
+
+#define TEST_CLOCK CLOCK_PROCESS_CPUTIME_ID
+#define TEST_CLOCK_MISSING(clock) \
+ (setup_test () ? "process CPU clock timer support" : NULL)
+
+/* This function is intended to rack up both user and system time. */
+static void *
+chew_cpu (void *arg)
+{
+ 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 = open ("/dev/null", O_WRONLY);
+ for (int i = 0; i < 100; ++i)
+ for (size_t j = 0; j < sizeof buf; ++j)
+ buf[j] = 0xbb;
+ write (nullfd, (char *) buf, sizeof buf);
+ close (nullfd);
+ }
+
+ return NULL;
+}
+
+static int
+setup_test (void)
+{
+ /* Test timers on our own process CPU clock by having a worker thread
+ eating CPU. First make sure we can make such timers at all. */
+
+ timer_t t;
+ if (timer_create (TEST_CLOCK, NULL, &t) != 0)
+ {
+ printf ("timer_create: %m\n");
+ return 1;
+ }
+ timer_delete (t);
+
+ pthread_t th;
+ int e = pthread_create (&th, NULL, chew_cpu, NULL);
+ if (e != 0)
+ {
+ printf ("pthread_create: %s\n", strerror (e));
+ exit (1);
+ }
+
+ return 0;
+}
+
+#else
+# define TEST_CLOCK_MISSING(clock) "process clocks"
+#endif
+
+#include "tst-timer4.c"
diff --git a/rt/tst-cputimer2.c b/rt/tst-cputimer2.c
new file mode 100644
index 0000000000..397d7998c0
--- /dev/null
+++ b/rt/tst-cputimer2.c
@@ -0,0 +1,83 @@
+/* Tests for POSIX timer implementation using thread CPU clock. */
+
+#include <unistd.h>
+
+#if _POSIX_THREADS && defined _POSIX_CPUTIME
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pthread.h>
+
+static clockid_t worker_thread_clock;
+
+#define TEST_CLOCK worker_thread_clock
+#define TEST_CLOCK_MISSING(clock) \
+ (setup_test () ? "thread CPU clock timer support" : NULL)
+
+/* This function is intended to rack up both user and system time. */
+static void *
+chew_cpu (void *arg)
+{
+ 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 = open ("/dev/null", O_WRONLY);
+ for (int i = 0; i < 100; ++i)
+ for (size_t j = 0; j < sizeof buf; ++j)
+ buf[j] = 0xbb;
+ write (nullfd, (char *) buf, sizeof buf);
+ close (nullfd);
+ }
+
+ return NULL;
+}
+
+static int
+setup_test (void)
+{
+ /* Test timers on a thread CPU clock by having a worker thread eating
+ CPU. First make sure we can make such timers at all. */
+
+ pthread_t th;
+ int e = pthread_create (&th, NULL, chew_cpu, NULL);
+ if (e != 0)
+ {
+ printf ("pthread_create: %s\n", strerror (e));
+ exit (1);
+ }
+
+ e = pthread_getcpuclockid (th, &worker_thread_clock);
+ if (e == EPERM || e == ENOENT || e == ENOTSUP)
+ {
+ puts ("pthread_getcpuclockid does not support other threads");
+ return 1;
+ }
+ if (e != 0)
+ {
+ printf ("pthread_getcpuclockid: %s\n", strerror (e));
+ exit (1);
+ }
+
+ timer_t t;
+ if (timer_create (TEST_CLOCK, NULL, &t) != 0)
+ {
+ printf ("timer_create: %m\n");
+ return 1;
+ }
+ timer_delete (t);
+
+ return 0;
+}
+
+#else
+# define TEST_CLOCK_MISSING(clock) "process clocks"
+#endif
+
+#include "tst-timer4.c"
diff --git a/rt/tst-cputimer3.c b/rt/tst-cputimer3.c
new file mode 100644
index 0000000000..056766a377
--- /dev/null
+++ b/rt/tst-cputimer3.c
@@ -0,0 +1,130 @@
+/* Tests for POSIX timer implementation using another process's CPU clock. */
+
+#include <unistd.h>
+
+#if _POSIX_THREADS && defined _POSIX_CPUTIME
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+static clockid_t child_clock;
+
+#define TEST_CLOCK child_clock
+#define TEST_CLOCK_MISSING(clock) \
+ (setup_test () ? "other-process CPU clock timer support" : NULL)
+
+/* This function is intended to rack up both user and system time. */
+static void
+chew_cpu (void)
+{
+ 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 = open ("/dev/null", O_WRONLY);
+ for (int i = 0; i < 100; ++i)
+ for (size_t j = 0; j < sizeof buf; ++j)
+ buf[j] = 0xbb;
+ write (nullfd, (char *) buf, sizeof buf);
+ close (nullfd);
+ if (getppid () == 1)
+ _exit (2);
+ }
+}
+
+static pid_t child;
+static void
+cleanup_child (void)
+{
+ if (child <= 0)
+ return;
+ if (kill (child, SIGKILL) < 0 && errno != ESRCH)
+ printf ("cannot kill child %d: %m\n", child);
+ else
+ {
+ int status;
+ errno = 0;
+ if (waitpid (child, &status, 0) != child)
+ printf ("waitpid %d: %m\n", child);
+ }
+}
+#define CLEANUP_HANDLER cleanup_child ()
+
+static int
+setup_test (void)
+{
+ /* Test timers on a process CPU clock by having a child process eating
+ CPU. First make sure we can make such timers at all. */
+
+ int pipefd[2];
+ if (pipe (pipefd) < 0)
+ {
+ printf ("pipe: %m\n");
+ exit (1);
+ }
+
+ child = fork ();
+
+ if (child == 0)
+ {
+ char c;
+ close (pipefd[1]);
+ if (read (pipefd[0], &c, 1) == 1)
+ chew_cpu ();
+ _exit (1);
+ }
+
+ if (child < 0)
+ {
+ printf ("fork: %m\n");
+ exit (1);
+ }
+
+ atexit (&cleanup_child);
+
+ close (pipefd[0]);
+
+ int e = clock_getcpuclockid (child, &child_clock);
+ if (e == EPERM)
+ {
+ puts ("clock_getcpuclockid does not support other processes");
+ return 1;
+ }
+ if (e != 0)
+ {
+ printf ("clock_getcpuclockid: %s\n", strerror (e));
+ exit (1);
+ }
+
+ timer_t t;
+ if (timer_create (TEST_CLOCK, NULL, &t) != 0)
+ {
+ printf ("timer_create: %m\n");
+ return 1;
+ }
+ timer_delete (t);
+
+ /* Get the child started chewing. */
+ if (write (pipefd[1], "x", 1) != 1)
+ {
+ printf ("write to pipe: %m\n");
+ return 1;
+ }
+ close (pipefd[1]);
+
+ return 0;
+}
+
+#else
+# define TEST_CLOCK_MISSING(clock) "process clocks"
+#endif
+
+#include "tst-timer4.c"