When a thread exits, run the destructors for the thread's specific data.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@330 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c
index 7fba3b0..d98b730 100644
--- a/coregrind/arch/x86-linux/vg_libpthread.c
+++ b/coregrind/arch/x86-linux/vg_libpthread.c
@@ -279,8 +279,10 @@
 __attribute__((noreturn))
 void thread_exit_wrapper ( void* ret_val )
 {
-   int          detached, res;
-   CleanupEntry cu;
+   int           detached, res;
+   CleanupEntry  cu;
+   pthread_key_t key;
+
    /* Run this thread's cleanup handlers. */
    while (1) {
       VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
@@ -292,7 +294,21 @@
       cu.fn ( cu.arg );
    }
 
-   /* Run this thread's key finalizers. */
+   /* Run this thread's key finalizers.  Really this should be run
+      PTHREAD_DESTRUCTOR_ITERATIONS times. */
+   for (key = 0; key < VG_N_THREAD_KEYS; key++) {
+      VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */,
+                              VG_USERREQ__GET_KEY_D_AND_S,
+                              key, &cu, 0, 0 );
+      if (res == 0) {
+         /* valid key */
+         if (cu.fn && cu.arg)
+            cu.fn /* destructor for key */ 
+                  ( cu.arg /* specific for key for this thread */ );
+         continue;
+      }
+      assert(res == -1);
+   }
 
    /* Decide on my final disposition. */
    VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */,
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index 6a078f6..290fbec 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -476,6 +476,7 @@
 
 #define VG_USERREQ__CLEANUP_PUSH            0x3020
 #define VG_USERREQ__CLEANUP_POP             0x3021
+#define VG_USERREQ__GET_KEY_D_AND_S         0x3022
 
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c
index 7fba3b0..d98b730 100644
--- a/coregrind/vg_libpthread.c
+++ b/coregrind/vg_libpthread.c
@@ -279,8 +279,10 @@
 __attribute__((noreturn))
 void thread_exit_wrapper ( void* ret_val )
 {
-   int          detached, res;
-   CleanupEntry cu;
+   int           detached, res;
+   CleanupEntry  cu;
+   pthread_key_t key;
+
    /* Run this thread's cleanup handlers. */
    while (1) {
       VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
@@ -292,7 +294,21 @@
       cu.fn ( cu.arg );
    }
 
-   /* Run this thread's key finalizers. */
+   /* Run this thread's key finalizers.  Really this should be run
+      PTHREAD_DESTRUCTOR_ITERATIONS times. */
+   for (key = 0; key < VG_N_THREAD_KEYS; key++) {
+      VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */,
+                              VG_USERREQ__GET_KEY_D_AND_S,
+                              key, &cu, 0, 0 );
+      if (res == 0) {
+         /* valid key */
+         if (cu.fn && cu.arg)
+            cu.fn /* destructor for key */ 
+                  ( cu.arg /* specific for key for this thread */ );
+         continue;
+      }
+      assert(res == -1);
+   }
 
    /* Decide on my final disposition. */
    VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */,
diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c
index 7eeee51..60c6c28 100644
--- a/coregrind/vg_scheduler.c
+++ b/coregrind/vg_scheduler.c
@@ -2672,7 +2672,8 @@
                  " increase and recompile");
    }
 
-   vg_thread_keys[i].inuse = True;
+   vg_thread_keys[i].inuse      = True;
+   vg_thread_keys[i].destructor = destructor;
 
    /* TODO: check key for addressibility */
    *key = i;
@@ -2762,6 +2763,33 @@
 }
 
 
+/* Helper for calling destructors at thread exit.  If key is valid,
+   copy the thread's specific value into cu->arg and put the *key*'s
+   destructor fn address in cu->fn.  Then return 0 to the caller.
+   Otherwise return non-zero to the caller. */
+static
+void do__get_key_destr_and_spec ( ThreadId tid, 
+                                  pthread_key_t key,
+                                  CleanupEntry* cu )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, 
+         "get_key_destr_and_arg (key = %d)", key );
+      print_pthread_event(tid, msg_buf);
+   }
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(key >= 0 && key < VG_N_THREAD_KEYS);
+   if (!vg_thread_keys[key].inuse) {
+      SET_EDX(tid, -1);
+      return;
+   }
+   cu->fn = vg_thread_keys[key].destructor;
+   cu->arg = VG_(threads)[tid].specifics[key];
+   SET_EDX(tid, 0);
+}
+
+
 /* ---------------------------------------------------
    SIGNALS
    ------------------------------------------------ */
@@ -2971,6 +2999,12 @@
                                         (void*)arg[2] );
          break;
 
+      case VG_USERREQ__GET_KEY_D_AND_S:
+         do__get_key_destr_and_spec ( tid, 
+                                      (pthread_key_t)arg[1],
+                                      (CleanupEntry*)arg[2] );
+         break;
+
       case VG_USERREQ__MAKE_NOACCESS:
       case VG_USERREQ__MAKE_WRITABLE:
       case VG_USERREQ__MAKE_READABLE:
diff --git a/vg_include.h b/vg_include.h
index 6a078f6..290fbec 100644
--- a/vg_include.h
+++ b/vg_include.h
@@ -476,6 +476,7 @@
 
 #define VG_USERREQ__CLEANUP_PUSH            0x3020
 #define VG_USERREQ__CLEANUP_POP             0x3021
+#define VG_USERREQ__GET_KEY_D_AND_S         0x3022
 
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
diff --git a/vg_libpthread.c b/vg_libpthread.c
index 7fba3b0..d98b730 100644
--- a/vg_libpthread.c
+++ b/vg_libpthread.c
@@ -279,8 +279,10 @@
 __attribute__((noreturn))
 void thread_exit_wrapper ( void* ret_val )
 {
-   int          detached, res;
-   CleanupEntry cu;
+   int           detached, res;
+   CleanupEntry  cu;
+   pthread_key_t key;
+
    /* Run this thread's cleanup handlers. */
    while (1) {
       VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */,
@@ -292,7 +294,21 @@
       cu.fn ( cu.arg );
    }
 
-   /* Run this thread's key finalizers. */
+   /* Run this thread's key finalizers.  Really this should be run
+      PTHREAD_DESTRUCTOR_ITERATIONS times. */
+   for (key = 0; key < VG_N_THREAD_KEYS; key++) {
+      VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */,
+                              VG_USERREQ__GET_KEY_D_AND_S,
+                              key, &cu, 0, 0 );
+      if (res == 0) {
+         /* valid key */
+         if (cu.fn && cu.arg)
+            cu.fn /* destructor for key */ 
+                  ( cu.arg /* specific for key for this thread */ );
+         continue;
+      }
+      assert(res == -1);
+   }
 
    /* Decide on my final disposition. */
    VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */,
diff --git a/vg_scheduler.c b/vg_scheduler.c
index 7eeee51..60c6c28 100644
--- a/vg_scheduler.c
+++ b/vg_scheduler.c
@@ -2672,7 +2672,8 @@
                  " increase and recompile");
    }
 
-   vg_thread_keys[i].inuse = True;
+   vg_thread_keys[i].inuse      = True;
+   vg_thread_keys[i].destructor = destructor;
 
    /* TODO: check key for addressibility */
    *key = i;
@@ -2762,6 +2763,33 @@
 }
 
 
+/* Helper for calling destructors at thread exit.  If key is valid,
+   copy the thread's specific value into cu->arg and put the *key*'s
+   destructor fn address in cu->fn.  Then return 0 to the caller.
+   Otherwise return non-zero to the caller. */
+static
+void do__get_key_destr_and_spec ( ThreadId tid, 
+                                  pthread_key_t key,
+                                  CleanupEntry* cu )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, 
+         "get_key_destr_and_arg (key = %d)", key );
+      print_pthread_event(tid, msg_buf);
+   }
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(key >= 0 && key < VG_N_THREAD_KEYS);
+   if (!vg_thread_keys[key].inuse) {
+      SET_EDX(tid, -1);
+      return;
+   }
+   cu->fn = vg_thread_keys[key].destructor;
+   cu->arg = VG_(threads)[tid].specifics[key];
+   SET_EDX(tid, 0);
+}
+
+
 /* ---------------------------------------------------
    SIGNALS
    ------------------------------------------------ */
@@ -2971,6 +2999,12 @@
                                         (void*)arg[2] );
          break;
 
+      case VG_USERREQ__GET_KEY_D_AND_S:
+         do__get_key_destr_and_spec ( tid, 
+                                      (pthread_key_t)arg[1],
+                                      (CleanupEntry*)arg[2] );
+         break;
+
       case VG_USERREQ__MAKE_NOACCESS:
       case VG_USERREQ__MAKE_WRITABLE:
       case VG_USERREQ__MAKE_READABLE: