summary refs log tree commit diff
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
glibc-84060bad8250d366c925f66bfba03efce9f4265c.tar.xz
glibc-84060bad8250d366c925f66bfba03efce9f4265c.zip
* 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"