Fix the handling of libc thread specific data so that it doesn't rely on
being able to use malloc as the libc malloc tries to use thread specific
data and we wind up in a recursive nightmare.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2293 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c
index 0f79e9f..8b362a6 100644
--- a/coregrind/vg_libpthread.c
+++ b/coregrind/vg_libpthread.c
@@ -192,12 +192,11 @@
    extern __locale_t __uselocale ( __locale_t );
 #endif
 
-static
-void init_thread_specific_state ( void );
+static void
+init_global_thread_specific_state ( void );
 
-static
-void init_libc_tsd_keys ( void );
-
+static void
+init_thread_specific_state ( void );
 
 /* ---------------------------------------------------------------------
    Helpers.  We have to be pretty self-sufficient.
@@ -831,15 +830,6 @@
    /* Initialise thread specific state */
    init_thread_specific_state();
 
-#  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
@@ -892,9 +882,10 @@
 
    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();
+   /* make sure the tsd keys, and hence locale info, for the root
+      thread are initialised before we get into complications making
+      new threads. */
+   init_global_thread_specific_state();
 
    /* Allocate space for the arg block.  thread_wrapper will free
       it. */
@@ -1869,6 +1860,16 @@
 
 #include <resolv.h>
 
+/* The allowable libc TSD keys (indices) from glibc source. */
+enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0,
+                        _LIBC_TSD_KEY_DL_ERROR,
+                        _LIBC_TSD_KEY_RPC_VARS,
+                        _LIBC_TSD_KEY_LOCALE,
+                        _LIBC_TSD_KEY_CTYPE_B,
+                        _LIBC_TSD_KEY_CTYPE_TOLOWER,
+                        _LIBC_TSD_KEY_CTYPE_TOUPPER,
+                        _LIBC_TSD_KEY_N };
+
 typedef
    struct {
      int                *errno_ptr;
@@ -1877,19 +1878,74 @@
      int                errno_data;
      int                h_errno_data;
      struct __res_state res_state_data;
+     void               *libc_specifics[_LIBC_TSD_KEY_N];
    }
    ThreadSpecificState;
 
 static ThreadSpecificState thread_specific_state[VG_N_THREADS];
 
-static
-void init_thread_specific_state ( void )
+/* Auto-initialising subsystem.  global_init_done is set 
+   after initialisation.  global_init_done_mx guards it. */
+static int             global_init_done    = 0;
+static pthread_mutex_t global_init_done_mx = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+init_global_thread_specific_state ( void )
 {
-  int tid = pthread_self();
-  
+   int res;
+
+   /* Don't fall into deadlock if we get called again whilst we still
+      hold the lock, via the __uselocale() call herein. */
+   if (global_init_done != 0)
+      return;
+
+   /* Take the lock. */
+   res = __pthread_mutex_lock(&global_init_done_mx);
+   if (res != 0) barf("init_global_thread_specific_state: lock");
+
+   /* Now test again, to be sure there is no mistake. */
+   if (global_init_done != 0) {
+      res = __pthread_mutex_unlock(&global_init_done_mx);
+      if (res != 0) barf("init_global_thread_specific_state: unlock(1)");
+      return;
+   }
+   
+   /* assert that we are the root thread. */
+   my_assert(pthread_self() == 1);
+
+   /* Initialise thread specific data for the root thread. */
+   init_thread_specific_state();
+
+   /* Signify init done. */
+   global_init_done = 1;
+
+   /* Unlock and return. */
+   res = __pthread_mutex_unlock(&global_init_done_mx);
+   if (res != 0) barf("init_global_thread_specific_state: unlock");
+}
+
+static void
+init_thread_specific_state ( void )
+{
+   int tid = pthread_self();
+   int i;
+
+   /* Initialise the errno and resolver state pointers. */
    thread_specific_state[tid].errno_ptr = NULL;
    thread_specific_state[tid].h_errno_ptr = NULL;
    thread_specific_state[tid].res_state_ptr = NULL;
+
+   /* Initialise the per-thread libc data. */
+   for (i = 0; i < _LIBC_TSD_KEY_N; i++) {
+      thread_specific_state[tid].libc_specifics[i] = NULL;
+   }
+
+#  ifdef GLIBC_2_3
+   /* Set this thread's locale to the global (default) locale.  A hack
+      in support of glibc-2.3.
+   */
+   __uselocale(LC_GLOBAL_LOCALE);
+#  endif
 }
 
 int* __errno_location ( void )
@@ -1964,110 +2020,28 @@
    LIBC-PRIVATE SPECIFIC DATA
    ------------------------------------------------ */
 
-/* Relies on assumption that initial private data is NULL.  This
-   should be fixed somehow. */
-
-/* The allowable keys (indices) (all 3 of them). 
-   From sysdeps/pthread/bits/libc-tsd.h
-*/
-/* as per glibc anoncvs HEAD of 20021001. */
-enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0,
-                        _LIBC_TSD_KEY_DL_ERROR,
-                        _LIBC_TSD_KEY_RPC_VARS,
-                        _LIBC_TSD_KEY_LOCALE,
-                        _LIBC_TSD_KEY_CTYPE_B,
-                        _LIBC_TSD_KEY_CTYPE_TOLOWER,
-                        _LIBC_TSD_KEY_CTYPE_TOUPPER,
-                        _LIBC_TSD_KEY_N };
-
-/* Auto-initialising subsystem.  libc_specifics_inited is set 
-   after initialisation.  libc_specifics_inited_mx guards it. */
-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 )
-{
-   int res, i;
-   pthread_key_t k;
-
-   /* Don't fall into deadlock if we get called again whilst we still
-      hold the lock, via the __uselocale() call herein. */
-   if (libc_specifics_inited != 0)
-      return;
-
-   /* Take the lock. */
-   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);
-      if (res != 0) barf("init_libc_tsd_keys: unlock(1)");
-      return;
-   }
-
-   /* Actually do the initialisation. */
-   /* printf("INIT libc specifics\n"); */
-   for (i = 0; i < _LIBC_TSD_KEY_N; i++) {
-      res = __pthread_key_create(&k, NULL);
-      if (res != 0) barf("init_libc_tsd_keys: create");
-      libc_specifics_keys[i] = k;
-   }
-
-   /* Signify init done. */
-   libc_specifics_inited = 1;
-
-#  ifdef GLIBC_2_3
-   /* Set the initialising thread's locale to the global (default)
-      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);
-   if (res != 0) barf("init_libc_tsd_keys: unlock");
-}
-
-
 static int
 libc_internal_tsd_set ( enum __libc_tsd_key_t key, 
                         const void * pointer )
 {
-   int res;
+   int tid = pthread_self();
    /* printf("SET SET SET key %d ptr %p\n", key, pointer); */
    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);
-   if (res != 0) barf("libc_internal_tsd_set: setspecific failed");
+   init_global_thread_specific_state();
+   thread_specific_state[tid].libc_specifics[key] = (void *)pointer;
    return 0;
 }
 
 static void *
 libc_internal_tsd_get ( enum __libc_tsd_key_t key )
 {
-   void* v;
+   int tid = pthread_self();
    /* printf("GET GET GET key %d\n", 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]);
-   /* if (v == NULL) barf("libc_internal_tsd_set: getspecific failed"); */
-   return v;
+   init_global_thread_specific_state();
+   return thread_specific_state[tid].libc_specifics[key];
 }
 
 
@@ -2085,13 +2059,12 @@
 static void**
 libc_internal_tsd_address ( enum __libc_tsd_key_t key )
 {
-   void** v;
+   int tid = pthread_self();
    /* 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;
+   init_global_thread_specific_state();
+   return &thread_specific_state[tid].libc_specifics[key];
 }
 
 void ** (*__libc_internal_tsd_address)