2002-08-02 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / linuxthreads / specific.c
index a86b1a0..caa6736 100644 (file)
 #include <stdlib.h>
 #include "pthread.h"
 #include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+#include <bits/libc-lock.h>
 
-typedef void (*destr_function)(void *);
 
 /* Table of keys. */
 
-struct pthread_key_struct {
-  int in_use;                   /* already allocated? */
-  destr_function destr;         /* destruction routine */
-};
-
 static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] =
   { { 0, NULL } };
 
+/* For debugging purposes put the maximum number of keys in a variable.  */
+const int __linuxthreads_pthread_keys_max = PTHREAD_KEYS_MAX;
+const int __linuxthreads_pthread_key_2ndlevel_size = PTHREAD_KEY_2NDLEVEL_SIZE;
+
 /* Mutex to protect access to pthread_keys */
 
 static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -58,13 +59,38 @@ int __pthread_key_create(pthread_key_t * key, destr_function destr)
 }
 strong_alias (__pthread_key_create, pthread_key_create)
 
-/* Delete a key */
+/* Reset deleted key's value to NULL in each live thread.
+ * NOTE: this executes in the context of the thread manager! */
+
+struct pthread_key_delete_helper_args {
+  /* Damn, we need lexical closures in C! ;) */
+  unsigned int idx1st, idx2nd;
+  pthread_descr self;
+};
+
+static void pthread_key_delete_helper(void *arg, pthread_descr th)
+{
+  struct pthread_key_delete_helper_args *args = arg;
+  unsigned int idx1st = args->idx1st;
+  unsigned int idx2nd = args->idx2nd;
+  pthread_descr self = args->self;
+
+  if (self == 0)
+    self = args->self = thread_self();
+
+  if (!th->p_terminated) {
+    /* pthread_exit() may try to free th->p_specific[idx1st] concurrently. */
+    __pthread_lock(THREAD_GETMEM(th, p_lock), self);
+    if (th->p_specific[idx1st] != NULL)
+      th->p_specific[idx1st][idx2nd] = NULL;
+    __pthread_unlock(THREAD_GETMEM(th, p_lock));
+  }
+}
 
+/* Delete a key */
 int pthread_key_delete(pthread_key_t key)
 {
   pthread_descr self = thread_self();
-  pthread_descr th;
-  unsigned int idx1st, idx2nd;
 
   pthread_mutex_lock(&pthread_keys_mutex);
   if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) {
@@ -73,17 +99,32 @@ int pthread_key_delete(pthread_key_t key)
   }
   pthread_keys[key].in_use = 0;
   pthread_keys[key].destr = NULL;
-  /* Set the value of the key to NULL in all running threads, so that
-     if the key is reallocated later by pthread_key_create, its
-     associated values will be NULL in all threads. */
-  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
-  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
-  th = self;
-  do {
-    if (th->p_specific[idx1st] != NULL)
-      th->p_specific[idx1st][idx2nd] = NULL;
-    th = th->p_nextlive;
-  } while (th != self);
+
+  /* Set the value of the key to NULL in all running threads, so
+     that if the key is reallocated later by pthread_key_create, its
+     associated values will be NULL in all threads.
+
+     Do nothing if no threads have been created yet.  */
+
+  if (__pthread_manager_request != -1)
+    {
+      struct pthread_key_delete_helper_args args;
+      struct pthread_request request;
+
+      args.idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
+      args.idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
+      args.self = 0;
+
+      request.req_thread = self;
+      request.req_kind = REQ_FOR_EACH_THREAD;
+      request.req_args.for_each.arg = &args;
+      request.req_args.for_each.fn = pthread_key_delete_helper;
+
+      TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request,
+                                     (char *) &request, sizeof(request)));
+      suspend(self);
+    }
+
   pthread_mutex_unlock(&pthread_keys_mutex);
   return 0;
 }
@@ -153,25 +194,39 @@ void __pthread_destroy_specifics()
           }
         }
   }
+  __pthread_lock(THREAD_GETMEM(self, p_lock), self);
   for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
-    if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL)
+    if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL) {
       free(THREAD_GETMEM_NC(self, p_specific[i]));
+      THREAD_SETMEM_NC(self, p_specific[i], NULL);
+    }
   }
+  __pthread_unlock(THREAD_GETMEM(self, p_lock));
 }
 
+#if !(USE_TLS && HAVE___THREAD)
+
 /* Thread-specific data for libc. */
 
-int __libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer)
+static int
+libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer)
 {
   pthread_descr self = thread_self();
 
   THREAD_SETMEM_NC(self, p_libc_specific[key], (void *) pointer);
   return 0;
 }
+int (*__libc_internal_tsd_set)(enum __libc_tsd_key_t key, const void * pointer)
+     = libc_internal_tsd_set;
 
-void * __libc_internal_tsd_get(enum __libc_tsd_key_t key)
+static void *
+libc_internal_tsd_get(enum __libc_tsd_key_t key)
 {
   pthread_descr self = thread_self();
 
   return THREAD_GETMEM_NC(self, p_libc_specific[key]);
 }
+void * (*__libc_internal_tsd_get)(enum __libc_tsd_key_t key)
+     = libc_internal_tsd_get;
+
+#endif