Update.
authorUlrich Drepper <drepper@redhat.com>
Wed, 25 Jun 2003 00:00:50 +0000 (00:00 +0000)
committerUlrich Drepper <drepper@redhat.com>
Wed, 25 Jun 2003 00:00:50 +0000 (00:00 +0000)
* 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.

14 files changed:
ChangeLog
include/time.h
nptl/ChangeLog
nptl/Makefile
nptl/allocatestack.c
nptl/pthreadP.h
nptl/pthread_clock_gettime.c
nptl/pthread_clock_settime.c
nptl/sysdeps/pthread/pthread_getcpuclockid.c
nptl/tst-clock2.c [new file with mode: 0644]
sysdeps/posix/clock_getres.c
sysdeps/unix/clock_gettime.c
sysdeps/unix/clock_nanosleep.c
sysdeps/unix/clock_settime.c

index f4f7a86..bca1fe7 100644 (file)
--- 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
index daf4c27..b501bfd 100644 (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
index c53fc8a..a39ae7e 100644 (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
index a6aa8c2..09a7ecc 100644 (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/
index 48a4720..223f0e4 100644 (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
index b67e0dd..1c523a8 100644 (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,
index 0e6f67b..a71174c 100644 (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>
 
 #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;
index ef6dc18..61002a8 100644 (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>
 
 
 #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
index 6386dc4..8506f94 100644 (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
diff --git a/nptl/tst-clock2.c b/nptl/tst-clock2.c
new file mode 100644 (file)
index 0000000..4c716b0
--- /dev/null
@@ -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"
index 91c5762..a2d4666 100644 (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;
index b8b2b74..7a3db29 100644 (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;
index 7662d17..6b170fd 100644 (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
index 069336a..6e1f487 100644 (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;