aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--include/time.h5
-rw-r--r--nptl/ChangeLog10
-rw-r--r--nptl/Makefile4
-rw-r--r--nptl/allocatestack.c47
-rw-r--r--nptl/pthreadP.h3
-rw-r--r--nptl/pthread_clock_gettime.c29
-rw-r--r--nptl/pthread_clock_settime.c32
-rw-r--r--nptl/sysdeps/pthread/pthread_getcpuclockid.c17
-rw-r--r--nptl/tst-clock2.c172
-rw-r--r--sysdeps/posix/clock_getres.c16
-rw-r--r--sysdeps/unix/clock_gettime.c23
-rw-r--r--sysdeps/unix/clock_nanosleep.c2
-rw-r--r--sysdeps/unix/clock_settime.c24
14 files changed, 355 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index f4f7a86f02..bca1fe7ac8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;