diff options
Diffstat (limited to 'rt')
-rw-r--r-- | rt/Makefile | 4 | ||||
-rw-r--r-- | rt/tst-cpuclock1.c | 307 | ||||
-rw-r--r-- | rt/tst-cpuclock2.c | 332 | ||||
-rw-r--r-- | rt/tst-cputimer1.c | 68 | ||||
-rw-r--r-- | rt/tst-cputimer2.c | 83 | ||||
-rw-r--r-- | rt/tst-cputimer3.c | 130 |
6 files changed, 923 insertions, 1 deletions
diff --git a/rt/Makefile b/rt/Makefile index 407cc99..cc01cb9 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 0000000..024df63 --- /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 0000000..d1621f3 --- /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 0000000..8f5dd76 --- /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 0000000..397d799 --- /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 0000000..056766a --- /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" |