diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | include/time.h | 5 | ||||
-rw-r--r-- | nptl/ChangeLog | 10 | ||||
-rw-r--r-- | nptl/Makefile | 4 | ||||
-rw-r--r-- | nptl/allocatestack.c | 47 | ||||
-rw-r--r-- | nptl/pthreadP.h | 3 | ||||
-rw-r--r-- | nptl/pthread_clock_gettime.c | 29 | ||||
-rw-r--r-- | nptl/pthread_clock_settime.c | 32 | ||||
-rw-r--r-- | nptl/sysdeps/pthread/pthread_getcpuclockid.c | 17 | ||||
-rw-r--r-- | nptl/tst-clock2.c | 172 | ||||
-rw-r--r-- | sysdeps/posix/clock_getres.c | 16 | ||||
-rw-r--r-- | sysdeps/unix/clock_gettime.c | 23 | ||||
-rw-r--r-- | sysdeps/unix/clock_nanosleep.c | 2 | ||||
-rw-r--r-- | sysdeps/unix/clock_settime.c | 24 |
14 files changed, 355 insertions, 36 deletions
@@ -1,5 +1,12 @@ 2003-06-24 Ulrich Drepper <drepper@redhat.com> + * include/time.h: Define CLOCK_IDFIELD_SIZE. + * sysdeps/posix/clock_getres.c: Recognize thread CPU clock IDs. + * sysdeps/unix/clock_gettime.c: Likewise. + * sysdeps/unix/clock_settime.c: Likewise. + * sysdeps/unix/clock_nanosleep.c (CPUCLOCK_P): Adjust for new + clock id for thread CPU clocks. + * sysdeps/unix/sysv/linux/fstatfs64.c (__fstatfs64): Add support for the fstatfs64 syscall. * sysdeps/unix/sysv/linux/statfs64.c (__statfs64): Add support for diff --git a/include/time.h b/include/time.h index daf4c27495..b501bfd3de 100644 --- a/include/time.h +++ b/include/time.h @@ -82,5 +82,10 @@ extern int __getdate_r (__const char *__string, struct tm *__resbufp); /* Determine CLK_TCK value. */ extern int __getclktck (void); + + +/* Use in the clock_* functions. Size of the field representing the + actual clock ID. */ +#define CLOCK_IDFIELD_SIZE 3 #endif #endif diff --git a/nptl/ChangeLog b/nptl/ChangeLog index c53fc8adf6..a39ae7eb7e 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,13 @@ +2003-06-24 Ulrich Drepper <drepper@redhat.com> + + * pthreadP.h: Declare __find_thread_by_id. + * allocatestack.c [HP_TIMING_AVAIL]: Define __find_thread_by_id. + * pthread_clock_gettime.c: Allow using other thread's clock. + * pthread_clock_settime.c: Likewise. + * sysdeps/pthread/pthread_getcpuclockid.c: Likewise. + * Makefile: Add rules to build and run tst-clock2. + * tst-clock2.c: New file. + 2003-06-23 Ulrich Drepper <drepper@redhat.com> * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S: Rewrite diff --git a/nptl/Makefile b/nptl/Makefile index a6aa8c2666..09a7ecc1b2 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -227,7 +227,7 @@ tests = tst-attr1 tst-attr2 \ tst-locale1 tst-locale2 \ tst-umask1 \ tst-popen1 \ - tst-clock1 \ + tst-clock1 tst-clock2 \ tst-context1 distribute = eintr.c @@ -391,12 +391,14 @@ $(objpfx)tst-cancel17: $(common-objpfx)rt/librt.so $(objpfx)tst-cancelx17: $(common-objpfx)rt/librt.so $(objpfx)tst-cancel18: $(common-objpfx)rt/librt.so $(objpfx)tst-cancelx18: $(common-objpfx)rt/librt.so +$(objpfx)tst-clock2: $(common-objpfx)rt/librt.so else $(objpfx)tst-cond11: $(common-objpfx)rt/librt.a $(objpfx)tst-cancel17: $(common-objpfx)rt/librt.a $(objpfx)tst-cancelx17: $(common-objpfx)rt/librt.a $(objpfx)tst-cancel18: $(common-objpfx)rt/librt.a $(objpfx)tst-cancelx18: $(common-objpfx)rt/librt.a +$(objpfx)tst-clock2: $(common-objpfx)rt/librt.a endif extra-B-pthread.so = -B$(common-objpfx)nptl/ diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 48a47205f8..223f0e445d 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -654,3 +654,50 @@ __reclaim_stacks (void) /* Initialize the lock. */ stack_cache_lock = LLL_LOCK_INITIALIZER; } + + +#if HP_TIMING_AVAIL +/* Find a thread given the thread ID. */ +struct pthread * +attribute_hidden +__find_thread_by_id (pid_t tid) +{ + struct pthread *result = NULL; + + lll_lock (stack_cache_lock); + + /* Iterate over the list with system-allocated threads first. */ + list_t *runp; + list_for_each (runp, &stack_used) + { + struct pthread *curp; + + curp = list_entry (runp, struct pthread, list); + + if (curp->tid == tid) + { + result = curp; + goto out; + } + } + + /* Now the list with threads using user-allocated stacks. */ + list_for_each (runp, &__stack_user) + { + struct pthread *curp; + + curp = list_entry (runp, struct pthread, list); + + if (curp->tid == tid) + { + result = curp; + goto out; + } + } + + out: + lll_unlock (stack_cache_lock); + + return result; +} +#endif diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index b67e0ddb65..1c523a873d 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -234,6 +234,9 @@ extern int __pthread_multiple_threads attribute_hidden; extern int *__libc_multiple_threads_ptr attribute_hidden; #endif +/* Find a thread given its TID. */ +extern struct pthread *__find_thread_by_id (pid_t tid) attribute_hidden; + /* Namespace save aliases. */ extern int __pthread_getschedparam (pthread_t thread_id, int *policy, diff --git a/nptl/pthread_clock_gettime.c b/nptl/pthread_clock_gettime.c index 0e6f67bc96..a71174c9ca 100644 --- a/nptl/pthread_clock_gettime.c +++ b/nptl/pthread_clock_gettime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. +/* Copyright (C) 2001, 2002, 2003 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 @@ -16,6 +16,7 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <errno.h> #include <stdlib.h> #include <time.h> #include <libc-internal.h> @@ -24,15 +25,37 @@ #if HP_TIMING_AVAIL int -__pthread_clock_gettime (hp_timing_t freq, struct timespec *tp) +__pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq, + struct timespec *tp) { hp_timing_t tsc; /* Get the current counter. */ HP_TIMING_NOW (tsc); + /* This is the ID of the thread we are looking for. */ + pid_t tid = ((unsigned int) clock_id) >> CLOCK_IDFIELD_SIZE; + /* Compute the offset since the start time of the process. */ - tsc -= THREAD_GETMEM (THREAD_SELF, cpuclock_offset); + if (tid == 0 || tid == THREAD_GETMEM (THREAD_SELF, tid)) + /* Our own clock. */ + tsc -= THREAD_GETMEM (THREAD_SELF, cpuclock_offset); + else + { + /* This is more complicated. We have to locate the thread based + on the ID. This means walking the list of existing + threads. */ + struct pthread *thread = __find_thread_by_id (tid); + if (thread == NULL) + { + __set_errno (EINVAL); + return -1; + } + + /* There is a race here. The thread might terminate and the stack + become unusable. But this is the user's problem. */ + tsc -= thread->cpuclock_offset; + } /* Compute the seconds. */ tp->tv_sec = tsc / freq; diff --git a/nptl/pthread_clock_settime.c b/nptl/pthread_clock_settime.c index ef6dc18773..61002a8f74 100644 --- a/nptl/pthread_clock_settime.c +++ b/nptl/pthread_clock_settime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. +/* Copyright (C) 2001, 2002, 2003 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 @@ -16,6 +16,7 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <errno.h> #include <stdlib.h> #include <time.h> #include <libc-internal.h> @@ -23,10 +24,33 @@ #if HP_TIMING_AVAIL -void -__pthread_clock_settime (hp_timing_t offset) +int +__pthread_clock_settime (clockid_t clock_id, hp_timing_t offset) { + /* This is the ID of the thread we are looking for. */ + pid_t tid = ((unsigned int) clock_id) >> CLOCK_IDFIELD_SIZE; + /* Compute the offset since the start time of the process. */ - THREAD_SETMEM (THREAD_SELF, cpuclock_offset, offset); + if (tid == 0 || tid == THREAD_GETMEM (THREAD_SELF, tid)) + /* Our own clock. */ + THREAD_SETMEM (THREAD_SELF, cpuclock_offset, offset); + else + { + /* This is more complicated. We have to locate the thread based + on the ID. This means walking the list of existing + threads. */ + struct pthread *thread = __find_thread_by_id (tid); + if (thread == NULL) + { + __set_errno (EINVAL); + return -1; + } + + /* There is a race here. The thread might terminate and the stack + become unusable. But this is the user's problem. */ + thread->cpuclock_offset = offset; + } + + return 0; } #endif diff --git a/nptl/sysdeps/pthread/pthread_getcpuclockid.c b/nptl/sysdeps/pthread/pthread_getcpuclockid.c index 6386dc4dc0..8506f94eb4 100644 --- a/nptl/sysdeps/pthread/pthread_getcpuclockid.c +++ b/nptl/sysdeps/pthread/pthread_getcpuclockid.c @@ -34,13 +34,20 @@ pthread_getcpuclockid (threadid, clockid) /* Not a valid thread handle. */ return ESRCH; - /* We don't allow any process ID but our own. */ - if (pd != THREAD_SELF) - return EPERM; - #ifdef CLOCK_THREAD_CPUTIME_ID + /* We need to store the thread ID in the CLOCKID variable together + with a number identifying the clock. We reserve the low 3 bits + for the clock ID and the rest for the thread ID. This is + problematic if the thread ID is too large. But 29 bits should be + fine. + + If some day more clock IDs are needed the ID part can be + enlarged. The IDs are entirely internal. */ + if (pd->tid >= 1 << (8 * sizeof (*clockid) - CLOCK_IDFIELD_SIZE)) + return ERANGE; + /* Store the number. */ - *clockid = CLOCK_THREAD_CPUTIME_ID; + *clockid = CLOCK_THREAD_CPUTIME_ID | (pd->tid << CLOCK_IDFIELD_SIZE); return 0; #else diff --git a/nptl/tst-clock2.c b/nptl/tst-clock2.c new file mode 100644 index 0000000000..4c716b0011 --- /dev/null +++ b/nptl/tst-clock2.c @@ -0,0 +1,172 @@ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2003. + + 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 <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + + +static pthread_barrier_t b2; +static pthread_barrier_t bN; + + +static void * +tf (void *arg) +{ + int e = pthread_barrier_wait (&b2); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("barrier_wait failed"); + exit (1); + } + + e = pthread_barrier_wait (&bN); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("barrier_wait failed"); + exit (1); + } + + return NULL; +} + + +int +do_test (void) +{ +#if _POSIX_THREAD_CPUTIME +# define N 10 + + if (pthread_barrier_init (&b2, NULL, 2) != 0 + || pthread_barrier_init (&bN, NULL, N + 1) != 0) + { + puts ("barrier_init failed"); + return 1; + } + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; + TEMP_FAILURE_RETRY (nanosleep (&ts, &ts)); + + pthread_t th[N + 1]; + clockid_t cl[N + 1]; +#ifndef CLOCK_THREAD_CPUTIME_ID + if (pthread_getcpuclockid (pthread_self (), &cl[0]) != 0) + { + puts ("own pthread_getcpuclockid failed"); + return 1; + } +#else + cl[0] = CLOCK_THREAD_CPUTIME_ID; +#endif + + int i; + int e; + for (i = 0; i < N; ++i) + { + if (pthread_create (&th[i], NULL, tf, NULL) != 0) + { + puts ("create failed"); + return 1; + } + + e = pthread_barrier_wait (&b2); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("barrier_wait failed"); + return 1; + } + + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + TEMP_FAILURE_RETRY (nanosleep (&ts, &ts)); + + if (pthread_getcpuclockid (th[i], &cl[i + 1]) != 0) + { + puts ("pthread_getcpuclockid failed"); + return 1; + } + } + + struct timespec t[N + 1]; + for (i = 0; i < N + 1; ++i) + if (clock_gettime (cl[i], &t[i]) != 0) + { + printf ("clock_gettime round %d failed\n", i); + return 1; + } + + for (i = 0; i < N; ++i) + { + struct timespec diff; + + diff.tv_sec = t[i].tv_sec - t[i + 1].tv_sec; + diff.tv_nsec = t[i].tv_nsec - t[i + 1].tv_nsec; + if (diff.tv_nsec < 0) + { + diff.tv_nsec += 1000000000; + --diff.tv_sec; + } + + if (diff.tv_sec < 0 || (diff.tv_sec == 0 && diff.tv_nsec < 100000000)) + { + printf ("\ +difference between thread %d and %d too small (%ld.%09ld)\n", + i, i + 1, (long int) diff.tv_sec, (long int) diff.tv_nsec); + return 1; + } + + printf ("diff %d->%d: %ld.%09ld\n", + i, i + 1, (long int) diff.tv_sec, (long int) diff.tv_nsec); + } + + ts.tv_sec = 0; + ts.tv_nsec = 0; + for (i = 0; i < N + 1; ++i) + if (clock_settime (cl[i], &ts) != 0) + { + printf ("clock_settime(%d) round %d failed\n", cl[i], i); + return 1; + } + + for (i = 0; i < N + 1; ++i) + { + if (clock_gettime (cl[i], &ts) != 0) + { + puts ("clock_gettime failed"); + return 1; + } + + if (ts.tv_sec > t[i].tv_sec + || (ts.tv_sec == t[i].tv_sec && ts.tv_nsec > t[i].tv_nsec)) + { + puts ("clock_settime didn't reset clock"); + return 1; + } + } +#endif + + return 0; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/sysdeps/posix/clock_getres.c b/sysdeps/posix/clock_getres.c index 91c57629ca..a2d466607e 100644 --- a/sysdeps/posix/clock_getres.c +++ b/sysdeps/posix/clock_getres.c @@ -64,9 +64,19 @@ clock_getres (clockid_t clock_id, struct timespec *res) break; #endif /* handled REALTIME */ + default: +#if HP_TIMING_AVAIL + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + != CLOCK_THREAD_CPUTIME_ID) +#endif + { + __set_errno (EINVAL); + break; + } + #if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME + /* FALLTHROUGH. */ case CLOCK_PROCESS_CPUTIME_ID: - case CLOCK_THREAD_CPUTIME_ID: { if (__builtin_expect (nsec == 0, 0)) { @@ -93,10 +103,6 @@ clock_getres (clockid_t clock_id, struct timespec *res) } break; #endif - - default: - __set_errno (EINVAL); - break; } return retval; diff --git a/sysdeps/unix/clock_gettime.c b/sysdeps/unix/clock_gettime.c index b8b2b74e2f..7a3db29744 100644 --- a/sysdeps/unix/clock_gettime.c +++ b/sysdeps/unix/clock_gettime.c @@ -32,7 +32,8 @@ static hp_timing_t freq; /* This function is defined in the thread library. */ -extern int __pthread_clock_gettime (hp_timing_t freq, struct timespec *tp) +extern int __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq, + struct timespec *tp) __attribute__ ((__weak__)); #endif @@ -64,9 +65,19 @@ clock_gettime (clockid_t clock_id, struct timespec *tp) break; #endif + default: +#if HP_TIMING_AVAIL + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + != CLOCK_THREAD_CPUTIME_ID) +#endif + { + __set_errno (EINVAL); + break; + } + #if HP_TIMING_AVAIL + /* FALLTHROUGH. */ case CLOCK_PROCESS_CPUTIME_ID: - case CLOCK_THREAD_CPUTIME_ID: { hp_timing_t tsc; @@ -82,10 +93,10 @@ clock_gettime (clockid_t clock_id, struct timespec *tp) break; } - if (clock_id == CLOCK_THREAD_CPUTIME_ID + if (clock_id != CLOCK_PROCESS_CPUTIME_ID && __pthread_clock_gettime != NULL) { - retval = __pthread_clock_gettime (freq, tp); + retval = __pthread_clock_gettime (clock_id, freq, tp); break; } @@ -106,10 +117,6 @@ clock_gettime (clockid_t clock_id, struct timespec *tp) } break; #endif - - default: - __set_errno (EINVAL); - break; } return retval; diff --git a/sysdeps/unix/clock_nanosleep.c b/sysdeps/unix/clock_nanosleep.c index 7662d1704e..6b170fd702 100644 --- a/sysdeps/unix/clock_nanosleep.c +++ b/sysdeps/unix/clock_nanosleep.c @@ -26,7 +26,7 @@ #if HP_TIMING_AVAIL # define CPUCLOCK_P(clock) \ ((clock) == CLOCK_PROCESS_CPUTIME_ID \ - || (clock) == CLOCK_THREAD_CPUTIME_ID) + || ((clock) & ((1 << CLOCK_IDFIELD_SIZE) - 1)) == CLOCK_THREAD_CPUTIME_ID) #else # define CPUCLOCK_P(clock) 0 #endif diff --git a/sysdeps/unix/clock_settime.c b/sysdeps/unix/clock_settime.c index 069336a69e..6e1f487171 100644 --- a/sysdeps/unix/clock_settime.c +++ b/sysdeps/unix/clock_settime.c @@ -31,7 +31,7 @@ static hp_timing_t freq; /* This function is defined in the thread library. */ -extern void __pthread_clock_settime (hp_timing_t offset) +extern void __pthread_clock_settime (clockid_t clock_id, hp_timing_t offset) __attribute__ ((__weak__)); #endif @@ -69,9 +69,20 @@ clock_settime (clockid_t clock_id, const struct timespec *tp) break; #endif + default: +#if HP_TIMING_AVAIL + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + != CLOCK_THREAD_CPUTIME_ID) +#endif + { + __set_errno (EINVAL); + retval = -1; + break; + } + #if HP_TIMING_AVAIL + /* FALLTHROUGH. */ case CLOCK_PROCESS_CPUTIME_ID: - case CLOCK_THREAD_CPUTIME_ID: { hp_timing_t tsc; hp_timing_t usertime; @@ -98,21 +109,16 @@ clock_settime (clockid_t clock_id, const struct timespec *tp) usertime = tp->tv_sec * freq + (tp->tv_nsec * freq) / 1000000000ull; /* Determine the offset and use it as the new base value. */ - if (clock_id != CLOCK_THREAD_CPUTIME_ID + if (clock_id == CLOCK_PROCESS_CPUTIME_ID || __pthread_clock_settime == NULL) GL(dl_cpuclock_offset) = tsc - usertime; else - __pthread_clock_settime (tsc - usertime); + __pthread_clock_settime (clock_id, tsc - usertime); retval = 0; } break; #endif - - default: - __set_errno (EINVAL); - retval = -1; - break; } return retval; |