]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Finish off support for Red Hat 8 thread-specific-data. Unfortunately
authorJulian Seward <jseward@acm.org>
Sat, 12 Oct 2002 16:42:35 +0000 (16:42 +0000)
committerJulian Seward <jseward@acm.org>
Sat, 12 Oct 2002 16:42:35 +0000 (16:42 +0000)
the appearance of libc_internal_tsd_address requires a redesign, since
the existing scheme doesn't make it sensible to take the address of a
specific-data value.

New scheme is that the ThreadState structure carries not the table of
specifics, but merely a pointer to such.  This is allocated from the
client-side library, thus residing in client-visible memory and so
addresses can validlyh be taken.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1208

coregrind/arch/x86-linux/vg_libpthread.c
coregrind/vg_include.h
coregrind/vg_libpthread.c
coregrind/vg_scheduler.c

index 70abb179dffda6f75e949043f9e679b4c2e1adcf..048f175bcafb9c39a1cb47e00644e26bfbd63ce0 100644 (file)
@@ -99,6 +99,9 @@ int my_do_syscall3 ( int syscallno,
    extern __locale_t __uselocale ( __locale_t );
 #endif
 
+static
+void init_libc_tsd_keys ( void );
+
 
 /* ---------------------------------------------------------------------
    Helpers.  We have to be pretty self-sufficient.
@@ -229,6 +232,27 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn )
                              __FILE__, __LINE__,                     \
                               __PRETTY_FUNCTION__), 0)))
 
+static
+void my_free ( void* ptr )
+{
+   int res;
+   VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
+                           VG_USERREQ__FREE, ptr, 0, 0, 0);
+   my_assert(res == 0);
+}
+
+
+static
+void* my_malloc ( int nbytes )
+{
+   void* res;
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__MALLOC, nbytes, 0, 0, 0);
+   my_assert(res != (void*)0);
+   return res;
+}
+
+
 
 /* ---------------------------------------------------------------------
    Pass pthread_ calls to Valgrind's request mechanism.
@@ -252,6 +276,24 @@ void pthread_error ( const char* msg )
 }
 
 
+/* ---------------------------------------------------
+   Here so it can be inlined without complaint.
+   ------------------------------------------------ */
+
+__inline__
+pthread_t pthread_self(void)
+{
+   int tid;
+   ensure_valgrind("pthread_self");
+   VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
+                           VG_USERREQ__PTHREAD_GET_THREADID,
+                           0, 0, 0, 0);
+   if (tid < 1 || tid >= VG_N_THREADS)
+      barf("pthread_self: invalid ThreadId");
+   return tid;
+}
+
+
 /* ---------------------------------------------------
    THREAD ATTRIBUTES
    ------------------------------------------------ */
@@ -443,6 +485,7 @@ void thread_exit_wrapper ( void* ret_val )
    int           detached, res;
    CleanupEntry  cu;
    pthread_key_t key;
+   void**        specifics_ptr;
 
    /* Run this thread's cleanup handlers. */
    while (1) {
@@ -471,6 +514,15 @@ void thread_exit_wrapper ( void* ret_val )
       my_assert(res == -1);
    }
 
+   /* Free up my specifics space, if any. */
+   VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */,
+                           VG_USERREQ__PTHREAD_GETSPECIFIC_PTR,
+                           pthread_self(), 0, 0, 0);
+   my_assert(specifics_ptr != (void**)3);
+   my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */
+   if (specifics_ptr != NULL)
+      my_free(specifics_ptr);
+
    /* Decide on my final disposition. */
    VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */,
                            VG_USERREQ__SET_OR_GET_DETACH, 
@@ -515,7 +567,6 @@ static
 __attribute__((noreturn))
 void thread_wrapper ( NewThreadInfo* info )
 {
-   int   res;
    int   attr__detachstate;
    void* (*root_fn) ( void* );
    void* arg;
@@ -526,9 +577,7 @@ void thread_wrapper ( NewThreadInfo* info )
    arg               = info->arg;
 
    /* Free up the arg block that pthread_create malloced. */
-   VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
-                           VG_USERREQ__FREE, info, 0, 0, 0);
-   my_assert(res == 0);
+   my_free(info);
 
    /* Minimally observe the attributes supplied. */
    if (attr__detachstate != PTHREAD_CREATE_DETACHED
@@ -537,6 +586,15 @@ void thread_wrapper ( NewThreadInfo* info )
    if (attr__detachstate == PTHREAD_CREATE_DETACHED)
       pthread_detach(pthread_self());
 
+#  ifdef GLIBC_2_3
+   /* Set this thread's locale to the global (default) locale.  A hack
+      in support of glibc-2.3.  This does the biz for the all new
+      threads; the root thread is done with a horrible hack in
+      init_libc_tsd_keys() below.
+   */
+   __uselocale(LC_GLOBAL_LOCALE);
+#  endif
+
    /* The root function might not return.  But if it does we simply
       move along to thread_exit_wrapper.  All other ways out for the
       thread (cancellation, or calling pthread_exit) lead there
@@ -581,11 +639,13 @@ pthread_create (pthread_t *__restrict __thredd,
 
    ensure_valgrind("pthread_create");
 
+   /* make sure the tsd keys, and hence locale info, are initialised
+      before we get into complications making new threads. */
+   init_libc_tsd_keys();
+
    /* Allocate space for the arg block.  thread_wrapper will free
       it. */
-   VALGRIND_MAGIC_SEQUENCE(info, NULL /* default */,
-                           VG_USERREQ__MALLOC, 
-                           sizeof(NewThreadInfo), 0, 0, 0);
+   info = my_malloc(sizeof(NewThreadInfo));
    my_assert(info != NULL);
 
    if (__attr)
@@ -626,19 +686,6 @@ void pthread_exit(void *retval)
 }
 
 
-pthread_t pthread_self(void)
-{
-   int tid;
-   ensure_valgrind("pthread_self");
-   VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */,
-                           VG_USERREQ__PTHREAD_GET_THREADID,
-                           0, 0, 0, 0);
-   if (tid < 1 || tid >= VG_N_THREADS)
-      barf("pthread_self: invalid ThreadId");
-   return tid;
-}
-
-
 int pthread_detach(pthread_t th)
 {
    int res;
@@ -1219,43 +1266,140 @@ int pause ( void )
    THREAD-SPECIFICs
    ------------------------------------------------ */
 
+static
+int key_is_valid (pthread_key_t key)
+{
+   int res;
+   VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */,
+                           VG_USERREQ__PTHREAD_KEY_VALIDATE,
+                           key, 0, 0, 0);
+   my_assert(res != 2);
+   return res;
+}
+
+
+/* Returns NULL if thread is invalid.  Otherwise, if the thread
+   already has a specifics area, return that.  Otherwise allocate it
+   one. */
+static
+void** get_or_allocate_specifics_ptr ( pthread_t thread )
+{
+   int    res, i;
+   void** specifics_ptr;
+   ensure_valgrind("get_or_allocate_specifics_ptr");
+
+   /* Returns zero if the thread has no specific_ptr.  One if thread
+      is invalid.  Otherwise, the specific_ptr value.  This is
+      allocated with my_malloc and so is aligned and cannot be
+      confused with 1 or 3. */
+   VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */,
+                           VG_USERREQ__PTHREAD_GETSPECIFIC_PTR,
+                           thread, 0, 0, 0);
+   my_assert(specifics_ptr != (void**)3);
+
+   if (specifics_ptr == (void**)1) 
+      return NULL; /* invalid thread */
+
+   if (specifics_ptr != NULL)
+      return specifics_ptr; /* already has a specifics ptr. */
+
+   /* None yet ... allocate a new one.  Should never fail. */
+   specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) );
+   my_assert(specifics_ptr != NULL);
+
+   VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */,
+                           VG_USERREQ__PTHREAD_SETSPECIFIC_PTR,
+                           specifics_ptr, 0, 0, 0);
+   my_assert(res == 0);
+
+   /* POSIX sez: "Upon thread creation, the value NULL shall be
+      associated with all defined keys in the new thread."  This
+      allocation is in effect a delayed allocation of the specific
+      data for a thread, at its first-use.  Hence we initialise it
+      here. */
+   for (i = 0; i < VG_N_THREAD_KEYS; i++) {
+      specifics_ptr[i] = NULL;
+   }
+
+   return specifics_ptr;   
+}
+
+
 int __pthread_key_create(pthread_key_t *key,  
                          void  (*destr_function)  (void *))
 {
-   int res;
+   void** specifics_ptr;
+   int    res, i;
    ensure_valgrind("pthread_key_create");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+
+   /* This writes *key if successful.  It should never fail. */
+   VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */,
                            VG_USERREQ__PTHREAD_KEY_CREATE,
                            key, destr_function, 0, 0);
+   my_assert(res == 0);
+
+   /* POSIX sez: "Upon key creation, the value NULL shall be
+      associated with the new key in all active threads." */
+   for (i = 0; i < VG_N_THREADS; i++) {
+      specifics_ptr = get_or_allocate_specifics_ptr(i);
+      /* we get NULL if i is an invalid thread. */
+      if (specifics_ptr != NULL)
+         specifics_ptr[*key] = NULL;
+   }
+
    return res;
 }
 
 int pthread_key_delete(pthread_key_t key)
 {
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      ignored("pthread_key_delete");
+   int res;
+   ensure_valgrind("pthread_key_create");
+   if (!key_is_valid(key))
+      return EINVAL;
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_KEY_DELETE,
+                           key, 0, 0, 0);
+   my_assert(res == 0);
    return 0;
 }
 
 int __pthread_setspecific(pthread_key_t key, const void *pointer)
 {
-   int res;
+   void** specifics_ptr;
    ensure_valgrind("pthread_setspecific");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_SETSPECIFIC,
-                           key, pointer, 0, 0);
-   return res;
+   
+   if (!key_is_valid(key))
+      return EINVAL;
+
+   specifics_ptr = get_or_allocate_specifics_ptr(pthread_self());
+   specifics_ptr[key] = (void*)pointer;
+   return 0;
 }
 
 void * __pthread_getspecific(pthread_key_t key)
 {
-   int res;
+   void** specifics_ptr;
    ensure_valgrind("pthread_getspecific");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_GETSPECIFIC,
-                           key, 0 , 0, 0);
-   return (void*)res;
+
+   if (!key_is_valid(key))
+      return NULL;
+
+   specifics_ptr = get_or_allocate_specifics_ptr(pthread_self());
+   return specifics_ptr[key];
+}
+
+
+static
+void ** __pthread_getspecific_addr(pthread_key_t key)
+{
+   void** specifics_ptr;
+   ensure_valgrind("pthread_getspecific_addr");
+
+   if (!key_is_valid(key))
+      return NULL;
+
+   specifics_ptr = get_or_allocate_specifics_ptr(pthread_self());
+   return &(specifics_ptr[key]);
 }
 
 
@@ -1424,9 +1568,11 @@ enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0,
 static int             libc_specifics_inited    = 0;
 static pthread_mutex_t libc_specifics_inited_mx = PTHREAD_MUTEX_INITIALIZER;
 
+
 /* These are the keys we must initialise the first time. */
 static pthread_key_t libc_specifics_keys[_LIBC_TSD_KEY_N];
 
+
 /* Initialise the keys, if they are not already initialised. */
 static
 void init_libc_tsd_keys ( void )
@@ -1440,12 +1586,12 @@ void init_libc_tsd_keys ( void )
       return;
 
    /* Take the lock. */
-   res = pthread_mutex_lock(&libc_specifics_inited_mx);
+   res = __pthread_mutex_lock(&libc_specifics_inited_mx);
    if (res != 0) barf("init_libc_tsd_keys: lock");
 
    /* Now test again, to be sure there is no mistake. */
    if (libc_specifics_inited != 0) {
-      res = pthread_mutex_unlock(&libc_specifics_inited_mx);
+      res = __pthread_mutex_unlock(&libc_specifics_inited_mx);
       if (res != 0) barf("init_libc_tsd_keys: unlock(1)");
       return;
    }
@@ -1453,7 +1599,7 @@ void init_libc_tsd_keys ( void )
    /* Actually do the initialisation. */
    /* printf("INIT libc specifics\n"); */
    for (i = 0; i < _LIBC_TSD_KEY_N; i++) {
-      res = pthread_key_create(&k, NULL);
+      res = __pthread_key_create(&k, NULL);
       if (res != 0) barf("init_libc_tsd_keys: create");
       libc_specifics_keys[i] = k;
    }
@@ -1463,12 +1609,19 @@ void init_libc_tsd_keys ( void )
 
 #  ifdef GLIBC_2_3
    /* Set the initialising thread's locale to the global (default)
-      locale.  A hack in support of glibc-2.3. */
+      locale.  A hack in support of glibc-2.3.  This does the biz for
+      the root thread.  For all other threads we run this in
+      thread_wrapper(), which does the real work of
+      pthread_create(). */
+   /* assert that we are the root thread.  I don't know if this is
+      really a valid assertion to make; if it breaks I'll reconsider
+      it. */
+   my_assert(pthread_self() == 1);
    __uselocale(LC_GLOBAL_LOCALE);
 #  endif
 
    /* Unlock and return. */
-   res = pthread_mutex_unlock(&libc_specifics_inited_mx);
+   res = __pthread_mutex_unlock(&libc_specifics_inited_mx);
    if (res != 0) barf("init_libc_tsd_keys: unlock");
 }
 
@@ -1482,7 +1635,7 @@ libc_internal_tsd_set ( enum __libc_tsd_key_t key,
    if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N)
       barf("libc_internal_tsd_set: invalid key");
    init_libc_tsd_keys();
-   res = pthread_setspecific(libc_specifics_keys[key], pointer);
+   res = __pthread_setspecific(libc_specifics_keys[key], pointer);
    if (res != 0) barf("libc_internal_tsd_set: setspecific failed");
    return 0;
 }
@@ -1495,13 +1648,12 @@ libc_internal_tsd_get ( enum __libc_tsd_key_t key )
    if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N)
       barf("libc_internal_tsd_get: invalid key");
    init_libc_tsd_keys();
-   v = pthread_getspecific(libc_specifics_keys[key]);
+   v = __pthread_getspecific(libc_specifics_keys[key]);
    /* if (v == NULL) barf("libc_internal_tsd_set: getspecific failed"); */
    return v;
 }
 
 
-
 int (*__libc_internal_tsd_set)
     (enum __libc_tsd_key_t key, const void * pointer)
    = libc_internal_tsd_set;
@@ -1511,6 +1663,26 @@ void* (*__libc_internal_tsd_get)
    = libc_internal_tsd_get;
 
 
+#ifdef GLIBC_2_3
+/* This one was first spotted be me in the glibc-2.2.93 sources. */
+static void**
+libc_internal_tsd_address ( enum __libc_tsd_key_t key )
+{
+   void** v;
+   /* printf("ADDR ADDR ADDR key %d\n", key); */
+   if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N)
+      barf("libc_internal_tsd_address: invalid key");
+   init_libc_tsd_keys();
+   v = __pthread_getspecific_addr(libc_specifics_keys[key]);
+   return v;
+}
+
+void ** (*__libc_internal_tsd_address) 
+        (enum __libc_tsd_key_t key)
+   = libc_internal_tsd_address;
+#endif
+
+
 /* ---------------------------------------------------------------------
    These are here (I think) because they are deemed cancellation
    points by POSIX.  For the moment we'll simply pass the call along
index 164e896610832fd121b713716b6defa6946fcb80..f9a7a40ec0facc3a08fa1efd22909ee4db093490 100644 (file)
@@ -360,13 +360,14 @@ extern Bool  VG_(is_empty_arena) ( ArenaId aid );
 #define VG_USERREQ__PTHREAD_COND_BROADCAST  0x3012
 #define VG_USERREQ__PTHREAD_KEY_CREATE      0x3013
 #define VG_USERREQ__PTHREAD_KEY_DELETE      0x3014
-#define VG_USERREQ__PTHREAD_SETSPECIFIC     0x3015
-#define VG_USERREQ__PTHREAD_GETSPECIFIC     0x3016
+#define VG_USERREQ__PTHREAD_SETSPECIFIC_PTR 0x3015
+#define VG_USERREQ__PTHREAD_GETSPECIFIC_PTR 0x3016
 #define VG_USERREQ__READ_MILLISECOND_TIMER  0x3017
 #define VG_USERREQ__PTHREAD_SIGMASK         0x3018
 #define VG_USERREQ__SIGWAIT                 0x3019
 #define VG_USERREQ__PTHREAD_KILL            0x301A
 #define VG_USERREQ__PTHREAD_YIELD           0x301B
+#define VG_USERREQ__PTHREAD_KEY_VALIDATE    0x301C
 
 #define VG_USERREQ__CLEANUP_PUSH            0x3020
 #define VG_USERREQ__CLEANUP_POP             0x3021
@@ -562,8 +563,13 @@ struct _ThreadState {
    Int          custack_used;
    CleanupEntry custack[VG_N_CLEANUPSTACK];
 
-   /* thread-specific data */
-   void* specifics[VG_N_THREAD_KEYS];
+   /* A pointer to the thread's-specific-data.  This is handled almost
+      entirely from vg_libpthread.c.  We just provide hooks to get and
+      set this ptr.  This is either NULL, indicating the thread has
+      read/written none of its specifics so far, OR points to a
+      void*[VG_N_THREAD_KEYS], allocated and deallocated in
+      vg_libpthread.c. */
+   void** specifics_ptr;
 
    /* This thread's blocked-signals mask.  Semantics is that for a
       signal to be delivered to this thread, the signal must not be
index 70abb179dffda6f75e949043f9e679b4c2e1adcf..048f175bcafb9c39a1cb47e00644e26bfbd63ce0 100644 (file)
@@ -99,6 +99,9 @@ int my_do_syscall3 ( int syscallno,
    extern __locale_t __uselocale ( __locale_t );
 #endif
 
+static
+void init_libc_tsd_keys ( void );
+
 
 /* ---------------------------------------------------------------------
    Helpers.  We have to be pretty self-sufficient.
@@ -229,6 +232,27 @@ void my_assert_fail ( Char* expr, Char* file, Int line, Char* fn )
                              __FILE__, __LINE__,                     \
                               __PRETTY_FUNCTION__), 0)))
 
+static
+void my_free ( void* ptr )
+{
+   int res;
+   VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
+                           VG_USERREQ__FREE, ptr, 0, 0, 0);
+   my_assert(res == 0);
+}
+
+
+static
+void* my_malloc ( int nbytes )
+{
+   void* res;
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__MALLOC, nbytes, 0, 0, 0);
+   my_assert(res != (void*)0);
+   return res;
+}
+
+
 
 /* ---------------------------------------------------------------------
    Pass pthread_ calls to Valgrind's request mechanism.
@@ -252,6 +276,24 @@ void pthread_error ( const char* msg )
 }
 
 
+/* ---------------------------------------------------
+   Here so it can be inlined without complaint.
+   ------------------------------------------------ */
+
+__inline__
+pthread_t pthread_self(void)
+{
+   int tid;
+   ensure_valgrind("pthread_self");
+   VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
+                           VG_USERREQ__PTHREAD_GET_THREADID,
+                           0, 0, 0, 0);
+   if (tid < 1 || tid >= VG_N_THREADS)
+      barf("pthread_self: invalid ThreadId");
+   return tid;
+}
+
+
 /* ---------------------------------------------------
    THREAD ATTRIBUTES
    ------------------------------------------------ */
@@ -443,6 +485,7 @@ void thread_exit_wrapper ( void* ret_val )
    int           detached, res;
    CleanupEntry  cu;
    pthread_key_t key;
+   void**        specifics_ptr;
 
    /* Run this thread's cleanup handlers. */
    while (1) {
@@ -471,6 +514,15 @@ void thread_exit_wrapper ( void* ret_val )
       my_assert(res == -1);
    }
 
+   /* Free up my specifics space, if any. */
+   VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */,
+                           VG_USERREQ__PTHREAD_GETSPECIFIC_PTR,
+                           pthread_self(), 0, 0, 0);
+   my_assert(specifics_ptr != (void**)3);
+   my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */
+   if (specifics_ptr != NULL)
+      my_free(specifics_ptr);
+
    /* Decide on my final disposition. */
    VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */,
                            VG_USERREQ__SET_OR_GET_DETACH, 
@@ -515,7 +567,6 @@ static
 __attribute__((noreturn))
 void thread_wrapper ( NewThreadInfo* info )
 {
-   int   res;
    int   attr__detachstate;
    void* (*root_fn) ( void* );
    void* arg;
@@ -526,9 +577,7 @@ void thread_wrapper ( NewThreadInfo* info )
    arg               = info->arg;
 
    /* Free up the arg block that pthread_create malloced. */
-   VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
-                           VG_USERREQ__FREE, info, 0, 0, 0);
-   my_assert(res == 0);
+   my_free(info);
 
    /* Minimally observe the attributes supplied. */
    if (attr__detachstate != PTHREAD_CREATE_DETACHED
@@ -537,6 +586,15 @@ void thread_wrapper ( NewThreadInfo* info )
    if (attr__detachstate == PTHREAD_CREATE_DETACHED)
       pthread_detach(pthread_self());
 
+#  ifdef GLIBC_2_3
+   /* Set this thread's locale to the global (default) locale.  A hack
+      in support of glibc-2.3.  This does the biz for the all new
+      threads; the root thread is done with a horrible hack in
+      init_libc_tsd_keys() below.
+   */
+   __uselocale(LC_GLOBAL_LOCALE);
+#  endif
+
    /* The root function might not return.  But if it does we simply
       move along to thread_exit_wrapper.  All other ways out for the
       thread (cancellation, or calling pthread_exit) lead there
@@ -581,11 +639,13 @@ pthread_create (pthread_t *__restrict __thredd,
 
    ensure_valgrind("pthread_create");
 
+   /* make sure the tsd keys, and hence locale info, are initialised
+      before we get into complications making new threads. */
+   init_libc_tsd_keys();
+
    /* Allocate space for the arg block.  thread_wrapper will free
       it. */
-   VALGRIND_MAGIC_SEQUENCE(info, NULL /* default */,
-                           VG_USERREQ__MALLOC, 
-                           sizeof(NewThreadInfo), 0, 0, 0);
+   info = my_malloc(sizeof(NewThreadInfo));
    my_assert(info != NULL);
 
    if (__attr)
@@ -626,19 +686,6 @@ void pthread_exit(void *retval)
 }
 
 
-pthread_t pthread_self(void)
-{
-   int tid;
-   ensure_valgrind("pthread_self");
-   VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */,
-                           VG_USERREQ__PTHREAD_GET_THREADID,
-                           0, 0, 0, 0);
-   if (tid < 1 || tid >= VG_N_THREADS)
-      barf("pthread_self: invalid ThreadId");
-   return tid;
-}
-
-
 int pthread_detach(pthread_t th)
 {
    int res;
@@ -1219,43 +1266,140 @@ int pause ( void )
    THREAD-SPECIFICs
    ------------------------------------------------ */
 
+static
+int key_is_valid (pthread_key_t key)
+{
+   int res;
+   VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */,
+                           VG_USERREQ__PTHREAD_KEY_VALIDATE,
+                           key, 0, 0, 0);
+   my_assert(res != 2);
+   return res;
+}
+
+
+/* Returns NULL if thread is invalid.  Otherwise, if the thread
+   already has a specifics area, return that.  Otherwise allocate it
+   one. */
+static
+void** get_or_allocate_specifics_ptr ( pthread_t thread )
+{
+   int    res, i;
+   void** specifics_ptr;
+   ensure_valgrind("get_or_allocate_specifics_ptr");
+
+   /* Returns zero if the thread has no specific_ptr.  One if thread
+      is invalid.  Otherwise, the specific_ptr value.  This is
+      allocated with my_malloc and so is aligned and cannot be
+      confused with 1 or 3. */
+   VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */,
+                           VG_USERREQ__PTHREAD_GETSPECIFIC_PTR,
+                           thread, 0, 0, 0);
+   my_assert(specifics_ptr != (void**)3);
+
+   if (specifics_ptr == (void**)1) 
+      return NULL; /* invalid thread */
+
+   if (specifics_ptr != NULL)
+      return specifics_ptr; /* already has a specifics ptr. */
+
+   /* None yet ... allocate a new one.  Should never fail. */
+   specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) );
+   my_assert(specifics_ptr != NULL);
+
+   VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */,
+                           VG_USERREQ__PTHREAD_SETSPECIFIC_PTR,
+                           specifics_ptr, 0, 0, 0);
+   my_assert(res == 0);
+
+   /* POSIX sez: "Upon thread creation, the value NULL shall be
+      associated with all defined keys in the new thread."  This
+      allocation is in effect a delayed allocation of the specific
+      data for a thread, at its first-use.  Hence we initialise it
+      here. */
+   for (i = 0; i < VG_N_THREAD_KEYS; i++) {
+      specifics_ptr[i] = NULL;
+   }
+
+   return specifics_ptr;   
+}
+
+
 int __pthread_key_create(pthread_key_t *key,  
                          void  (*destr_function)  (void *))
 {
-   int res;
+   void** specifics_ptr;
+   int    res, i;
    ensure_valgrind("pthread_key_create");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+
+   /* This writes *key if successful.  It should never fail. */
+   VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */,
                            VG_USERREQ__PTHREAD_KEY_CREATE,
                            key, destr_function, 0, 0);
+   my_assert(res == 0);
+
+   /* POSIX sez: "Upon key creation, the value NULL shall be
+      associated with the new key in all active threads." */
+   for (i = 0; i < VG_N_THREADS; i++) {
+      specifics_ptr = get_or_allocate_specifics_ptr(i);
+      /* we get NULL if i is an invalid thread. */
+      if (specifics_ptr != NULL)
+         specifics_ptr[*key] = NULL;
+   }
+
    return res;
 }
 
 int pthread_key_delete(pthread_key_t key)
 {
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      ignored("pthread_key_delete");
+   int res;
+   ensure_valgrind("pthread_key_create");
+   if (!key_is_valid(key))
+      return EINVAL;
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_KEY_DELETE,
+                           key, 0, 0, 0);
+   my_assert(res == 0);
    return 0;
 }
 
 int __pthread_setspecific(pthread_key_t key, const void *pointer)
 {
-   int res;
+   void** specifics_ptr;
    ensure_valgrind("pthread_setspecific");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_SETSPECIFIC,
-                           key, pointer, 0, 0);
-   return res;
+   
+   if (!key_is_valid(key))
+      return EINVAL;
+
+   specifics_ptr = get_or_allocate_specifics_ptr(pthread_self());
+   specifics_ptr[key] = (void*)pointer;
+   return 0;
 }
 
 void * __pthread_getspecific(pthread_key_t key)
 {
-   int res;
+   void** specifics_ptr;
    ensure_valgrind("pthread_getspecific");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_GETSPECIFIC,
-                           key, 0 , 0, 0);
-   return (void*)res;
+
+   if (!key_is_valid(key))
+      return NULL;
+
+   specifics_ptr = get_or_allocate_specifics_ptr(pthread_self());
+   return specifics_ptr[key];
+}
+
+
+static
+void ** __pthread_getspecific_addr(pthread_key_t key)
+{
+   void** specifics_ptr;
+   ensure_valgrind("pthread_getspecific_addr");
+
+   if (!key_is_valid(key))
+      return NULL;
+
+   specifics_ptr = get_or_allocate_specifics_ptr(pthread_self());
+   return &(specifics_ptr[key]);
 }
 
 
@@ -1424,9 +1568,11 @@ enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0,
 static int             libc_specifics_inited    = 0;
 static pthread_mutex_t libc_specifics_inited_mx = PTHREAD_MUTEX_INITIALIZER;
 
+
 /* These are the keys we must initialise the first time. */
 static pthread_key_t libc_specifics_keys[_LIBC_TSD_KEY_N];
 
+
 /* Initialise the keys, if they are not already initialised. */
 static
 void init_libc_tsd_keys ( void )
@@ -1440,12 +1586,12 @@ void init_libc_tsd_keys ( void )
       return;
 
    /* Take the lock. */
-   res = pthread_mutex_lock(&libc_specifics_inited_mx);
+   res = __pthread_mutex_lock(&libc_specifics_inited_mx);
    if (res != 0) barf("init_libc_tsd_keys: lock");
 
    /* Now test again, to be sure there is no mistake. */
    if (libc_specifics_inited != 0) {
-      res = pthread_mutex_unlock(&libc_specifics_inited_mx);
+      res = __pthread_mutex_unlock(&libc_specifics_inited_mx);
       if (res != 0) barf("init_libc_tsd_keys: unlock(1)");
       return;
    }
@@ -1453,7 +1599,7 @@ void init_libc_tsd_keys ( void )
    /* Actually do the initialisation. */
    /* printf("INIT libc specifics\n"); */
    for (i = 0; i < _LIBC_TSD_KEY_N; i++) {
-      res = pthread_key_create(&k, NULL);
+      res = __pthread_key_create(&k, NULL);
       if (res != 0) barf("init_libc_tsd_keys: create");
       libc_specifics_keys[i] = k;
    }
@@ -1463,12 +1609,19 @@ void init_libc_tsd_keys ( void )
 
 #  ifdef GLIBC_2_3
    /* Set the initialising thread's locale to the global (default)
-      locale.  A hack in support of glibc-2.3. */
+      locale.  A hack in support of glibc-2.3.  This does the biz for
+      the root thread.  For all other threads we run this in
+      thread_wrapper(), which does the real work of
+      pthread_create(). */
+   /* assert that we are the root thread.  I don't know if this is
+      really a valid assertion to make; if it breaks I'll reconsider
+      it. */
+   my_assert(pthread_self() == 1);
    __uselocale(LC_GLOBAL_LOCALE);
 #  endif
 
    /* Unlock and return. */
-   res = pthread_mutex_unlock(&libc_specifics_inited_mx);
+   res = __pthread_mutex_unlock(&libc_specifics_inited_mx);
    if (res != 0) barf("init_libc_tsd_keys: unlock");
 }
 
@@ -1482,7 +1635,7 @@ libc_internal_tsd_set ( enum __libc_tsd_key_t key,
    if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N)
       barf("libc_internal_tsd_set: invalid key");
    init_libc_tsd_keys();
-   res = pthread_setspecific(libc_specifics_keys[key], pointer);
+   res = __pthread_setspecific(libc_specifics_keys[key], pointer);
    if (res != 0) barf("libc_internal_tsd_set: setspecific failed");
    return 0;
 }
@@ -1495,13 +1648,12 @@ libc_internal_tsd_get ( enum __libc_tsd_key_t key )
    if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N)
       barf("libc_internal_tsd_get: invalid key");
    init_libc_tsd_keys();
-   v = pthread_getspecific(libc_specifics_keys[key]);
+   v = __pthread_getspecific(libc_specifics_keys[key]);
    /* if (v == NULL) barf("libc_internal_tsd_set: getspecific failed"); */
    return v;
 }
 
 
-
 int (*__libc_internal_tsd_set)
     (enum __libc_tsd_key_t key, const void * pointer)
    = libc_internal_tsd_set;
@@ -1511,6 +1663,26 @@ void* (*__libc_internal_tsd_get)
    = libc_internal_tsd_get;
 
 
+#ifdef GLIBC_2_3
+/* This one was first spotted be me in the glibc-2.2.93 sources. */
+static void**
+libc_internal_tsd_address ( enum __libc_tsd_key_t key )
+{
+   void** v;
+   /* printf("ADDR ADDR ADDR key %d\n", key); */
+   if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N)
+      barf("libc_internal_tsd_address: invalid key");
+   init_libc_tsd_keys();
+   v = __pthread_getspecific_addr(libc_specifics_keys[key]);
+   return v;
+}
+
+void ** (*__libc_internal_tsd_address) 
+        (enum __libc_tsd_key_t key)
+   = libc_internal_tsd_address;
+#endif
+
+
 /* ---------------------------------------------------------------------
    These are here (I think) because they are deemed cancellation
    points by POSIX.  For the moment we'll simply pass the call along
index d257f9f357432754a2a15372f61c5ad6ab54d8fe..acdb9f37505fc4d48daa241f3e4da55c60fddd4d 100644 (file)
@@ -610,7 +610,6 @@ void increment_epoch ( void )
 static 
 void mostly_clear_thread_record ( ThreadId tid )
 {
-   Int j;
    vg_assert(tid >= 0 && tid < VG_N_THREADS);
    VG_(threads)[tid].ldt                  = NULL;
    VG_(threads)[tid].tid                  = tid;
@@ -629,8 +628,7 @@ void mostly_clear_thread_record ( ThreadId tid )
    VG_(threads)[tid].n_signals_returned = 0;
    VG_(ksigemptyset)(&VG_(threads)[tid].sig_mask);
    VG_(ksigemptyset)(&VG_(threads)[tid].sigs_waited_for);
-   for (j = 0; j < VG_N_THREAD_KEYS; j++)
-      VG_(threads)[tid].specifics[j] = NULL;
+   VG_(threads)[tid].specifics_ptr = NULL;
 }
 
 
@@ -2799,6 +2797,32 @@ Bool is_valid_key ( ThreadKey k )
    return True;
 }
 
+
+/* Return in %EDX a value of 1 if the key is valid, else 0. */
+static
+void do_pthread_key_validate ( ThreadId tid,
+                               pthread_key_t key )
+{
+   Char msg_buf[100];
+
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, "pthread_key_validate    key %p", 
+                            key );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && VG_(threads)[tid].status == VgTs_Runnable);
+
+   if (is_valid_key((ThreadKey)key)) {
+      SET_EDX(tid, 1);
+   } else {
+      SET_EDX(tid, 0);
+   }
+}
+
+
 static
 void do_pthread_key_create ( ThreadId tid,
                              pthread_key_t* key,
@@ -2864,66 +2888,54 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key )
    }
 
    vg_thread_keys[key].inuse = False;
-
-   /* Optional.  We're not required to do this, although it shouldn't
-      make any difference to programs which use the key/specifics
-      functions correctly.  */
-#  if 1
-   for (tid = 1; tid < VG_N_THREADS; tid++) {
-      if (VG_(threads)[tid].status != VgTs_Empty)
-         VG_(threads)[tid].specifics[key] = NULL;
-   }
-#  endif
+   SET_EDX(tid, 0);
 }
 
 
+/* Get the .specific_ptr for a thread.  Return 1 if the thread-slot
+   isn't in use, so that client-space can scan all thread slots.  1
+   cannot be confused with NULL or a legitimately-aligned specific_ptr
+   value. */
 static 
-void do_pthread_getspecific ( ThreadId tid, pthread_key_t key )
+void do_pthread_getspecific_ptr ( ThreadId tid )
 {
-   Char msg_buf[100];
+   void** specifics_ptr;
+   Char   msg_buf[100];
+
    if (VG_(clo_trace_pthread_level) >= 1) {
-      VG_(sprintf)(msg_buf, "pthread_getspecific      key %d", 
-                            key );
+      VG_(sprintf)(msg_buf, "pthread_getspecific_ptr" );
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(VG_(is_valid_tid)(tid) 
-             && VG_(threads)[tid].status == VgTs_Runnable);
+   vg_assert(VG_(is_valid_or_empty_tid)(tid));
 
-   if (!is_valid_key(key)) {
-      VG_(record_pthread_error)( tid, 
-         "pthread_getspecific: key is invalid");
-      SET_EDX(tid, (UInt)NULL);
+   if (VG_(threads)[tid].status == VgTs_Empty) {
+      SET_EDX(tid, 1);
       return;
    }
 
-   SET_EDX(tid, (UInt)VG_(threads)[tid].specifics[key]);
+   specifics_ptr = VG_(threads)[tid].specifics_ptr;
+   vg_assert(specifics_ptr == NULL 
+             || IS_ALIGNED4_ADDR(specifics_ptr));
+
+   SET_EDX(tid, (UInt)specifics_ptr);
 }
 
 
 static
-void do_pthread_setspecific ( ThreadId tid, 
-                              pthread_key_t key, 
-                              void *pointer )
+void do_pthread_setspecific_ptr ( ThreadId tid, void** ptr )
 {
    Char msg_buf[100];
    if (VG_(clo_trace_pthread_level) >= 1) {
-      VG_(sprintf)(msg_buf, "pthread_setspecific      key %d, ptr %p", 
-                            key, pointer );
+      VG_(sprintf)(msg_buf, "pthread_setspecific_ptr  ptr %p", 
+                            ptr );
       print_pthread_event(tid, msg_buf);
    }
 
    vg_assert(VG_(is_valid_tid)(tid) 
              && VG_(threads)[tid].status == VgTs_Runnable);
 
-   if (!is_valid_key(key)) {
-      VG_(record_pthread_error)( tid, 
-         "pthread_setspecific: key is invalid");
-      SET_EDX(tid, EINVAL);
-      return;
-   }
-
-   VG_(threads)[tid].specifics[key] = pointer;
+   VG_(threads)[tid].specifics_ptr = ptr;
    SET_EDX(tid, 0);
 }
 
@@ -2951,10 +2963,20 @@ void do__get_key_destr_and_spec ( ThreadId tid,
       return;
    }
    VG_TRACK( pre_mem_write, Vg_CorePThread, & VG_(threads)[tid], 
-                            "get_key_destr_and_spec", (Addr)cu,
+                            "get_key_destr_and_spec: cu", (Addr)cu,
                             sizeof(CleanupEntry) );
+
    cu->fn = vg_thread_keys[key].destructor;
-   cu->arg = VG_(threads)[tid].specifics[key];
+   if (VG_(threads)[tid].specifics_ptr == NULL) {
+      cu->arg = NULL;
+   } else {
+      VG_TRACK( pre_mem_read, Vg_CorePThread, & VG_(threads)[tid],
+                "get_key_destr_and_spec: key",
+                (Addr)(&VG_(threads)[tid].specifics_ptr[key]), 
+                sizeof(void*) );
+      cu->arg = VG_(threads)[tid].specifics_ptr[key];
+   }
+
    VG_TRACK( post_mem_write, (Addr)cu, sizeof(CleanupEntry) );
    SET_EDX(tid, 0);
 }
@@ -3266,8 +3288,8 @@ void do_client_request ( ThreadId tid )
          do_pthread_mutex_unlock( tid, (void *)(arg[1]) );
          break;
 
-      case VG_USERREQ__PTHREAD_GETSPECIFIC:
-        do_pthread_getspecific ( tid, (UInt)(arg[1]) );
+      case VG_USERREQ__PTHREAD_GETSPECIFIC_PTR:
+        do_pthread_getspecific_ptr ( tid );
          break;
 
       case VG_USERREQ__SET_CANCELTYPE:
@@ -3322,6 +3344,11 @@ void do_client_request ( ThreadId tid )
             (pthread_cond_t *)(arg[1]) );
          break;
 
+      case VG_USERREQ__PTHREAD_KEY_VALIDATE:
+        do_pthread_key_validate ( tid, 
+                                   (pthread_key_t)(arg[1]) );
+        break;
+
       case VG_USERREQ__PTHREAD_KEY_CREATE:
         do_pthread_key_create ( tid, 
                                  (pthread_key_t*)(arg[1]),
@@ -3333,10 +3360,9 @@ void do_client_request ( ThreadId tid )
                                  (pthread_key_t)(arg[1]) );
         break;
 
-      case VG_USERREQ__PTHREAD_SETSPECIFIC:
-        do_pthread_setspecific ( tid, 
-                                  (pthread_key_t)(arg[1]),
-                                 (void*)(arg[2]) );
+      case VG_USERREQ__PTHREAD_SETSPECIFIC_PTR:
+        do_pthread_setspecific_ptr ( tid, 
+                                     (void**)(arg[1]) );
         break;
 
       case VG_USERREQ__PTHREAD_SIGMASK: