* 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.
This commit is contained in:
Ulrich Drepper 2003-06-25 00:00:50 +00:00
parent 51d1ca00fd
commit 4165d44d70
14 changed files with 355 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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/

View File

@ -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

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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

172
nptl/tst-clock2.c Normal file
View File

@ -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"

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;