]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Sanity check mutex implementation, and add support for recursive mutexes.
authorJulian Seward <jseward@acm.org>
Wed, 17 Apr 2002 23:21:37 +0000 (23:21 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 17 Apr 2002 23:21:37 +0000 (23:21 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@94

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

index 11f5200a0d2e01821e2592d54b24c6c4732696e5..f32c5533eaa60d03c6d61654fa2fe50052d26cd5 100644 (file)
@@ -23,7 +23,6 @@
 #include "valgrind.h"    /* For the request-passing mechanism */
 #include "vg_include.h"  /* For the VG_USERREQ__* constants */
 
-#include <pthread.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -116,6 +115,14 @@ static void ignored ( char* msg )
    Pass pthread_ calls to Valgrind's request mechanism.
    ------------------------------------------------------------------ */
 
+#include <pthread.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* ---------------------------------------------------
+   THREAD ATTRIBUTES
+   ------------------------------------------------ */
+
 int pthread_attr_init(pthread_attr_t *attr)
 {
    ignored("pthread_attr_init");
@@ -129,6 +136,11 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
 }
 
 
+
+/* ---------------------------------------------------
+   THREADs
+   ------------------------------------------------ */
+
 int
 pthread_create (pthread_t *__restrict __thread,
                 __const pthread_attr_t *__restrict __attr,
@@ -157,20 +169,6 @@ pthread_join (pthread_t __th, void **__thread_return)
 }
 
 
-/* What are these?  Anybody know?  I don't. */
-
-void _pthread_cleanup_push_defer ( void )
-{
-  //  char* str = "_pthread_cleanup_push_defer\n";
-  //  write(2, str, strlen(str));
-}
-
-void _pthread_cleanup_pop_restore ( void )
-{
-  //  char* str = "_pthread_cleanup_pop_restore\n";
-  //  write(2, str, strlen(str));
-}
-
 
 static int thread_specific_errno[VG_N_THREADS];
 
@@ -188,33 +186,49 @@ int* __errno_location ( void )
 }
 
 
+/* ---------------------------------------------------
+   MUTEX ATTRIBUTES
+   ------------------------------------------------ */
+
 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
 {
-   ignored("pthread_mutexattr_init");
+   attr->__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP;
    return 0;
 }
 
-int pthread_mutex_init(pthread_mutex_t *mutex, 
-                       const  pthread_mutexattr_t *mutexattr)
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
 {
-   int res;
-   ensure_valgrind("pthread_mutex_init");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_MUTEX_INIT,
-                           mutex, mutexattr, 0, 0);
-   return res;
+   switch (type) {
+      case PTHREAD_MUTEX_TIMED_NP:
+      case PTHREAD_MUTEX_RECURSIVE_NP:
+      case PTHREAD_MUTEX_ERRORCHECK_NP:
+      case PTHREAD_MUTEX_ADAPTIVE_NP:
+         attr->__mutexkind = type;
+         return 0;
+      default:
+         return EINVAL;
+   }
 }
 
 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
 {
-   ignored("pthread_mutexattr_destroy");
    return 0;
 }
 
-int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
+
+/* ---------------------------------------------------
+   MUTEXes
+   ------------------------------------------------ */
+
+int pthread_mutex_init(pthread_mutex_t *mutex, 
+                       const  pthread_mutexattr_t *mutexattr)
 {
-   ignored("pthread_mutexattr_settype");
-   return 0;
+   int res;
+   ensure_valgrind("pthread_mutex_init");
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_MUTEX_INIT,
+                           mutex, mutexattr, 0, 0);
+   return res;
 }
 
 int pthread_mutex_lock(pthread_mutex_t *mutex)
@@ -249,18 +263,6 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex)
    }
 }
 
-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 < 0 || tid >= VG_N_THREADS)
-      barf("pthread_self: invalid ThreadId");
-   return tid;
-}
-
 int pthread_mutex_destroy(pthread_mutex_t *mutex)
 {
    int res;
@@ -278,6 +280,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
 }
 
 
+/* ---------------------------------------------------
+   CANCELLATION
+   ------------------------------------------------ */
+
 int pthread_setcanceltype(int type, int *oldtype)
 {
    ignored("pthread_setcanceltype");
@@ -296,6 +302,9 @@ int pthread_cancel(pthread_t thread)
 }
 
 
+/* ---------------------------------------------------
+   THREAD-SPECIFICs
+   ------------------------------------------------ */
 
 int pthread_key_create(pthread_key_t *key,  
                        void  (*destr_function)  (void *))
@@ -322,16 +331,47 @@ void * pthread_getspecific(pthread_key_t key)
    return NULL;
 }
 
+
+/* ---------------------------------------------------
+   MISC
+   ------------------------------------------------ */
+
+/* What are these?  Anybody know?  I don't. */
+
+void _pthread_cleanup_push_defer ( void )
+{
+  //  char* str = "_pthread_cleanup_push_defer\n";
+  //  write(2, str, strlen(str));
+}
+
+void _pthread_cleanup_pop_restore ( void )
+{
+  //  char* str = "_pthread_cleanup_pop_restore\n";
+  //  write(2, str, strlen(str));
+}
+
+
+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 < 0 || tid >= VG_N_THREADS)
+      barf("pthread_self: invalid ThreadId");
+   return tid;
+}
+
+
 /* ---------------------------------------------------------------------
    These are here (I think) because they are deemed cancellation
    points by POSIX.  For the moment we'll simply pass the call along
    to the corresponding thread-unaware (?) libc routine.
    ------------------------------------------------------------------ */
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
index 11f5200a0d2e01821e2592d54b24c6c4732696e5..f32c5533eaa60d03c6d61654fa2fe50052d26cd5 100644 (file)
@@ -23,7 +23,6 @@
 #include "valgrind.h"    /* For the request-passing mechanism */
 #include "vg_include.h"  /* For the VG_USERREQ__* constants */
 
-#include <pthread.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -116,6 +115,14 @@ static void ignored ( char* msg )
    Pass pthread_ calls to Valgrind's request mechanism.
    ------------------------------------------------------------------ */
 
+#include <pthread.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* ---------------------------------------------------
+   THREAD ATTRIBUTES
+   ------------------------------------------------ */
+
 int pthread_attr_init(pthread_attr_t *attr)
 {
    ignored("pthread_attr_init");
@@ -129,6 +136,11 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
 }
 
 
+
+/* ---------------------------------------------------
+   THREADs
+   ------------------------------------------------ */
+
 int
 pthread_create (pthread_t *__restrict __thread,
                 __const pthread_attr_t *__restrict __attr,
@@ -157,20 +169,6 @@ pthread_join (pthread_t __th, void **__thread_return)
 }
 
 
-/* What are these?  Anybody know?  I don't. */
-
-void _pthread_cleanup_push_defer ( void )
-{
-  //  char* str = "_pthread_cleanup_push_defer\n";
-  //  write(2, str, strlen(str));
-}
-
-void _pthread_cleanup_pop_restore ( void )
-{
-  //  char* str = "_pthread_cleanup_pop_restore\n";
-  //  write(2, str, strlen(str));
-}
-
 
 static int thread_specific_errno[VG_N_THREADS];
 
@@ -188,33 +186,49 @@ int* __errno_location ( void )
 }
 
 
+/* ---------------------------------------------------
+   MUTEX ATTRIBUTES
+   ------------------------------------------------ */
+
 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
 {
-   ignored("pthread_mutexattr_init");
+   attr->__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP;
    return 0;
 }
 
-int pthread_mutex_init(pthread_mutex_t *mutex, 
-                       const  pthread_mutexattr_t *mutexattr)
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
 {
-   int res;
-   ensure_valgrind("pthread_mutex_init");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_MUTEX_INIT,
-                           mutex, mutexattr, 0, 0);
-   return res;
+   switch (type) {
+      case PTHREAD_MUTEX_TIMED_NP:
+      case PTHREAD_MUTEX_RECURSIVE_NP:
+      case PTHREAD_MUTEX_ERRORCHECK_NP:
+      case PTHREAD_MUTEX_ADAPTIVE_NP:
+         attr->__mutexkind = type;
+         return 0;
+      default:
+         return EINVAL;
+   }
 }
 
 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
 {
-   ignored("pthread_mutexattr_destroy");
    return 0;
 }
 
-int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
+
+/* ---------------------------------------------------
+   MUTEXes
+   ------------------------------------------------ */
+
+int pthread_mutex_init(pthread_mutex_t *mutex, 
+                       const  pthread_mutexattr_t *mutexattr)
 {
-   ignored("pthread_mutexattr_settype");
-   return 0;
+   int res;
+   ensure_valgrind("pthread_mutex_init");
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_MUTEX_INIT,
+                           mutex, mutexattr, 0, 0);
+   return res;
 }
 
 int pthread_mutex_lock(pthread_mutex_t *mutex)
@@ -249,18 +263,6 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex)
    }
 }
 
-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 < 0 || tid >= VG_N_THREADS)
-      barf("pthread_self: invalid ThreadId");
-   return tid;
-}
-
 int pthread_mutex_destroy(pthread_mutex_t *mutex)
 {
    int res;
@@ -278,6 +280,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
 }
 
 
+/* ---------------------------------------------------
+   CANCELLATION
+   ------------------------------------------------ */
+
 int pthread_setcanceltype(int type, int *oldtype)
 {
    ignored("pthread_setcanceltype");
@@ -296,6 +302,9 @@ int pthread_cancel(pthread_t thread)
 }
 
 
+/* ---------------------------------------------------
+   THREAD-SPECIFICs
+   ------------------------------------------------ */
 
 int pthread_key_create(pthread_key_t *key,  
                        void  (*destr_function)  (void *))
@@ -322,16 +331,47 @@ void * pthread_getspecific(pthread_key_t key)
    return NULL;
 }
 
+
+/* ---------------------------------------------------
+   MISC
+   ------------------------------------------------ */
+
+/* What are these?  Anybody know?  I don't. */
+
+void _pthread_cleanup_push_defer ( void )
+{
+  //  char* str = "_pthread_cleanup_push_defer\n";
+  //  write(2, str, strlen(str));
+}
+
+void _pthread_cleanup_pop_restore ( void )
+{
+  //  char* str = "_pthread_cleanup_pop_restore\n";
+  //  write(2, str, strlen(str));
+}
+
+
+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 < 0 || tid >= VG_N_THREADS)
+      barf("pthread_self: invalid ThreadId");
+   return tid;
+}
+
+
 /* ---------------------------------------------------------------------
    These are here (I think) because they are deemed cancellation
    points by POSIX.  For the moment we'll simply pass the call along
    to the corresponding thread-unaware (?) libc routine.
    ------------------------------------------------------------------ */
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
index 6e354e28432e3280412e1730deb692258ab1f109..fadde58fc4834a081468995a68e7bd561e1d1d53 100644 (file)
@@ -110,10 +110,14 @@ typedef
    struct {
       /* Is this slot in use, or free? */
       Bool in_use;
-      /* If in_use, is this mutex held by some thread, or not? */
-      Bool held;
-      /* if held==True, owner indicates who by. */
+      /* When == 0, indicated not locked.
+         When > 0, number of times it has been locked by the owner. */
+      UInt count; 
+      /* if count > 0, owner indicates who by. */
       ThreadId owner;
+      /* True if a recursive mutex.  When False, count must never
+         exceed 1. */
+      Bool is_rec;
    }
    VgMutex;
 
@@ -1470,21 +1474,27 @@ void do_pthread_create ( ThreadId parent_tid,
 */
 
 static
-void initialise_mutex ( ThreadId tid, pthread_mutex_t *mutex )
+void initialise_mutex ( ThreadId tid, 
+                        pthread_mutex_t *mutex )
 {
    MutexId  mid;
    Char     msg_buf[100];
+   Bool     is_rec;
    /* vg_alloc_MutexId aborts if we can't allocate a mutex, for
       whatever reason. */
    mid = vg_alloc_VgMutex();
    vg_mutexes[mid].in_use = True;
-   vg_mutexes[mid].held = False;
+   vg_mutexes[mid].count = 0;
    vg_mutexes[mid].owner = VG_INVALID_THREADID; /* irrelevant */
+   is_rec = mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP;
+   vg_mutexes[mid].is_rec = is_rec;
+   vg_mutexes[mid].count  = 0;
    mutex->__m_reserved = mid;
    mutex->__m_count = 1; /* initialised */
    if (VG_(clo_trace_pthread_level) >= 1) {
-      VG_(sprintf)(msg_buf, "(initialise mutex) (%p) -> %d", 
-                            mutex, mid );
+      VG_(sprintf)(msg_buf, "(initialise mutex) (%p, %s) -> %d", 
+                            mutex, is_rec ? "RECURSIVE" : "NORMAL",
+                            mid );
       print_pthread_event(tid, msg_buf);
    }
 }
@@ -1500,6 +1510,8 @@ void do_pthread_mutex_init ( ThreadId tid,
    /* Paranoia ... */
    vg_assert(sizeof(pthread_mutex_t) >= sizeof(UInt));
 
+   if (mutexattr) 
+      mutex->__m_kind = mutexattr->__mutexkind;
    initialise_mutex(tid, mutex);
 
    if (VG_(clo_trace_pthread_level) >= 1) {
@@ -1537,6 +1549,8 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex )
    }
 
    if (mutex->__m_count == 0) {
+      /* The mutex->__m_kind will have been set by the static
+         initialisation. */
       initialise_mutex(tid, mutex);
    }
 
@@ -1558,27 +1572,43 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex )
    /* Assume tid valid. */
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
-   if (vg_mutexes[mid].held) {
+   if (vg_mutexes[mid].count > 0) {
+
+      /* Someone has it already. */
       if (vg_mutexes[mid].owner == tid) {
-         vg_threads[tid].m_edx = EDEADLK;
+         /* It's locked -- by me! */
+         if (vg_mutexes[mid].is_rec) {
+            /* return 0 (success). */
+            vg_mutexes[mid].count++;
+            vg_threads[tid].m_edx = 0;
+           VG_(printf)("!!!!!! tid %d, mutex %d -> locked %d\n", 
+                        tid, mid, vg_mutexes[mid].count);
+            return;
+         } else {
+            vg_threads[tid].m_edx = EDEADLK;
+            return;
+         }
+      } else {
+         /* Someone else has it; we have to wait. */
+         vg_threads[tid].status = VgTs_WaitMX;
+         vg_threads[tid].waited_on_mid = mid;
+         /* No assignment to %EDX, since we're blocking. */
+         if (VG_(clo_trace_pthread_level) >= 1) {
+            VG_(sprintf)(msg_buf, "pthread_mutex_lock   %d: BLOCK", 
+                                  mid );
+            print_pthread_event(tid, msg_buf);
+         }
          return;
       }
-      /* Someone else has it; we have to wait. */
-      vg_threads[tid].status = VgTs_WaitMX;
-      vg_threads[tid].waited_on_mid = mid;
-      /* No assignment to %EDX, since we're blocking. */
-      if (VG_(clo_trace_pthread_level) >= 1) {
-         VG_(sprintf)(msg_buf, "pthread_mutex_lock   %d: BLOCK", 
-                               mid );
-         print_pthread_event(tid, msg_buf);
-      }
+
    } else {
-      /* We get it! */
-      vg_mutexes[mid].held  = True;
+      /* We get it! [for the first time]. */
+      vg_mutexes[mid].count = 1;
       vg_mutexes[mid].owner = tid;
       /* return 0 (success). */
       vg_threads[tid].m_edx = 0;
    }
+
 }
 
 
@@ -1612,11 +1642,25 @@ void do_pthread_mutex_unlock ( ThreadId tid,
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    /* Barf if we don't currently hold the mutex. */
-   if (!vg_mutexes[mid].held || vg_mutexes[mid].owner != tid) {
+   if (vg_mutexes[mid].count == 0 /* nobody holds it */
+       || vg_mutexes[mid].owner != tid /* we don't hold it */) {
       vg_threads[tid].m_edx = EPERM;
       return;
    }
 
+   /* If it's a multiply-locked recursive mutex, just decrement the
+      lock count and return. */
+   if (vg_mutexes[mid].count > 1) {
+      vg_assert(vg_mutexes[mid].is_rec);
+      vg_mutexes[mid].count --;
+      vg_threads[tid].m_edx = 0; /* success */
+      return;
+   }
+
+   /* Now we're sure mid is locked exactly once, and by the thread who
+      is now doing an unlock on it.  */
+   vg_assert(vg_mutexes[mid].count == 1);
+
    /* Find some arbitrary thread waiting on this mutex, and make it
       runnable.  If none are waiting, mark the mutex as not held. */
    for (i = 0; i < VG_N_THREADS; i++) {
@@ -1630,10 +1674,11 @@ void do_pthread_mutex_unlock ( ThreadId tid,
    vg_assert(i <= VG_N_THREADS);
    if (i == VG_N_THREADS) {
       /* Nobody else is waiting on it. */
-      vg_mutexes[mid].held = False;
+      vg_mutexes[mid].count = 0;
    } else {
       /* Notionally transfer the hold to thread i, whose
          pthread_mutex_lock() call now returns with 0 (success). */
+      /* The .count is already == 1. */
       vg_mutexes[mid].owner = i;
       vg_threads[i].status = VgTs_Runnable;
       vg_threads[i].m_edx = 0; /* pth_lock() success */
@@ -1679,7 +1724,7 @@ static void do_pthread_mutex_destroy ( ThreadId tid,
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    /* Barf if the mutex is currently held. */
-   if (vg_mutexes[mid].held) {
+   if (vg_mutexes[mid].count > 0) {
       vg_threads[tid].m_edx = EBUSY;
       return;
    }
index 11f5200a0d2e01821e2592d54b24c6c4732696e5..f32c5533eaa60d03c6d61654fa2fe50052d26cd5 100644 (file)
@@ -23,7 +23,6 @@
 #include "valgrind.h"    /* For the request-passing mechanism */
 #include "vg_include.h"  /* For the VG_USERREQ__* constants */
 
-#include <pthread.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -116,6 +115,14 @@ static void ignored ( char* msg )
    Pass pthread_ calls to Valgrind's request mechanism.
    ------------------------------------------------------------------ */
 
+#include <pthread.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* ---------------------------------------------------
+   THREAD ATTRIBUTES
+   ------------------------------------------------ */
+
 int pthread_attr_init(pthread_attr_t *attr)
 {
    ignored("pthread_attr_init");
@@ -129,6 +136,11 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
 }
 
 
+
+/* ---------------------------------------------------
+   THREADs
+   ------------------------------------------------ */
+
 int
 pthread_create (pthread_t *__restrict __thread,
                 __const pthread_attr_t *__restrict __attr,
@@ -157,20 +169,6 @@ pthread_join (pthread_t __th, void **__thread_return)
 }
 
 
-/* What are these?  Anybody know?  I don't. */
-
-void _pthread_cleanup_push_defer ( void )
-{
-  //  char* str = "_pthread_cleanup_push_defer\n";
-  //  write(2, str, strlen(str));
-}
-
-void _pthread_cleanup_pop_restore ( void )
-{
-  //  char* str = "_pthread_cleanup_pop_restore\n";
-  //  write(2, str, strlen(str));
-}
-
 
 static int thread_specific_errno[VG_N_THREADS];
 
@@ -188,33 +186,49 @@ int* __errno_location ( void )
 }
 
 
+/* ---------------------------------------------------
+   MUTEX ATTRIBUTES
+   ------------------------------------------------ */
+
 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
 {
-   ignored("pthread_mutexattr_init");
+   attr->__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP;
    return 0;
 }
 
-int pthread_mutex_init(pthread_mutex_t *mutex, 
-                       const  pthread_mutexattr_t *mutexattr)
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
 {
-   int res;
-   ensure_valgrind("pthread_mutex_init");
-   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
-                           VG_USERREQ__PTHREAD_MUTEX_INIT,
-                           mutex, mutexattr, 0, 0);
-   return res;
+   switch (type) {
+      case PTHREAD_MUTEX_TIMED_NP:
+      case PTHREAD_MUTEX_RECURSIVE_NP:
+      case PTHREAD_MUTEX_ERRORCHECK_NP:
+      case PTHREAD_MUTEX_ADAPTIVE_NP:
+         attr->__mutexkind = type;
+         return 0;
+      default:
+         return EINVAL;
+   }
 }
 
 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
 {
-   ignored("pthread_mutexattr_destroy");
    return 0;
 }
 
-int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
+
+/* ---------------------------------------------------
+   MUTEXes
+   ------------------------------------------------ */
+
+int pthread_mutex_init(pthread_mutex_t *mutex, 
+                       const  pthread_mutexattr_t *mutexattr)
 {
-   ignored("pthread_mutexattr_settype");
-   return 0;
+   int res;
+   ensure_valgrind("pthread_mutex_init");
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_MUTEX_INIT,
+                           mutex, mutexattr, 0, 0);
+   return res;
 }
 
 int pthread_mutex_lock(pthread_mutex_t *mutex)
@@ -249,18 +263,6 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex)
    }
 }
 
-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 < 0 || tid >= VG_N_THREADS)
-      barf("pthread_self: invalid ThreadId");
-   return tid;
-}
-
 int pthread_mutex_destroy(pthread_mutex_t *mutex)
 {
    int res;
@@ -278,6 +280,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex)
 }
 
 
+/* ---------------------------------------------------
+   CANCELLATION
+   ------------------------------------------------ */
+
 int pthread_setcanceltype(int type, int *oldtype)
 {
    ignored("pthread_setcanceltype");
@@ -296,6 +302,9 @@ int pthread_cancel(pthread_t thread)
 }
 
 
+/* ---------------------------------------------------
+   THREAD-SPECIFICs
+   ------------------------------------------------ */
 
 int pthread_key_create(pthread_key_t *key,  
                        void  (*destr_function)  (void *))
@@ -322,16 +331,47 @@ void * pthread_getspecific(pthread_key_t key)
    return NULL;
 }
 
+
+/* ---------------------------------------------------
+   MISC
+   ------------------------------------------------ */
+
+/* What are these?  Anybody know?  I don't. */
+
+void _pthread_cleanup_push_defer ( void )
+{
+  //  char* str = "_pthread_cleanup_push_defer\n";
+  //  write(2, str, strlen(str));
+}
+
+void _pthread_cleanup_pop_restore ( void )
+{
+  //  char* str = "_pthread_cleanup_pop_restore\n";
+  //  write(2, str, strlen(str));
+}
+
+
+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 < 0 || tid >= VG_N_THREADS)
+      barf("pthread_self: invalid ThreadId");
+   return tid;
+}
+
+
 /* ---------------------------------------------------------------------
    These are here (I think) because they are deemed cancellation
    points by POSIX.  For the moment we'll simply pass the call along
    to the corresponding thread-unaware (?) libc routine.
    ------------------------------------------------------------------ */
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
index 6e354e28432e3280412e1730deb692258ab1f109..fadde58fc4834a081468995a68e7bd561e1d1d53 100644 (file)
@@ -110,10 +110,14 @@ typedef
    struct {
       /* Is this slot in use, or free? */
       Bool in_use;
-      /* If in_use, is this mutex held by some thread, or not? */
-      Bool held;
-      /* if held==True, owner indicates who by. */
+      /* When == 0, indicated not locked.
+         When > 0, number of times it has been locked by the owner. */
+      UInt count; 
+      /* if count > 0, owner indicates who by. */
       ThreadId owner;
+      /* True if a recursive mutex.  When False, count must never
+         exceed 1. */
+      Bool is_rec;
    }
    VgMutex;
 
@@ -1470,21 +1474,27 @@ void do_pthread_create ( ThreadId parent_tid,
 */
 
 static
-void initialise_mutex ( ThreadId tid, pthread_mutex_t *mutex )
+void initialise_mutex ( ThreadId tid, 
+                        pthread_mutex_t *mutex )
 {
    MutexId  mid;
    Char     msg_buf[100];
+   Bool     is_rec;
    /* vg_alloc_MutexId aborts if we can't allocate a mutex, for
       whatever reason. */
    mid = vg_alloc_VgMutex();
    vg_mutexes[mid].in_use = True;
-   vg_mutexes[mid].held = False;
+   vg_mutexes[mid].count = 0;
    vg_mutexes[mid].owner = VG_INVALID_THREADID; /* irrelevant */
+   is_rec = mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP;
+   vg_mutexes[mid].is_rec = is_rec;
+   vg_mutexes[mid].count  = 0;
    mutex->__m_reserved = mid;
    mutex->__m_count = 1; /* initialised */
    if (VG_(clo_trace_pthread_level) >= 1) {
-      VG_(sprintf)(msg_buf, "(initialise mutex) (%p) -> %d", 
-                            mutex, mid );
+      VG_(sprintf)(msg_buf, "(initialise mutex) (%p, %s) -> %d", 
+                            mutex, is_rec ? "RECURSIVE" : "NORMAL",
+                            mid );
       print_pthread_event(tid, msg_buf);
    }
 }
@@ -1500,6 +1510,8 @@ void do_pthread_mutex_init ( ThreadId tid,
    /* Paranoia ... */
    vg_assert(sizeof(pthread_mutex_t) >= sizeof(UInt));
 
+   if (mutexattr) 
+      mutex->__m_kind = mutexattr->__mutexkind;
    initialise_mutex(tid, mutex);
 
    if (VG_(clo_trace_pthread_level) >= 1) {
@@ -1537,6 +1549,8 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex )
    }
 
    if (mutex->__m_count == 0) {
+      /* The mutex->__m_kind will have been set by the static
+         initialisation. */
       initialise_mutex(tid, mutex);
    }
 
@@ -1558,27 +1572,43 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex )
    /* Assume tid valid. */
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
-   if (vg_mutexes[mid].held) {
+   if (vg_mutexes[mid].count > 0) {
+
+      /* Someone has it already. */
       if (vg_mutexes[mid].owner == tid) {
-         vg_threads[tid].m_edx = EDEADLK;
+         /* It's locked -- by me! */
+         if (vg_mutexes[mid].is_rec) {
+            /* return 0 (success). */
+            vg_mutexes[mid].count++;
+            vg_threads[tid].m_edx = 0;
+           VG_(printf)("!!!!!! tid %d, mutex %d -> locked %d\n", 
+                        tid, mid, vg_mutexes[mid].count);
+            return;
+         } else {
+            vg_threads[tid].m_edx = EDEADLK;
+            return;
+         }
+      } else {
+         /* Someone else has it; we have to wait. */
+         vg_threads[tid].status = VgTs_WaitMX;
+         vg_threads[tid].waited_on_mid = mid;
+         /* No assignment to %EDX, since we're blocking. */
+         if (VG_(clo_trace_pthread_level) >= 1) {
+            VG_(sprintf)(msg_buf, "pthread_mutex_lock   %d: BLOCK", 
+                                  mid );
+            print_pthread_event(tid, msg_buf);
+         }
          return;
       }
-      /* Someone else has it; we have to wait. */
-      vg_threads[tid].status = VgTs_WaitMX;
-      vg_threads[tid].waited_on_mid = mid;
-      /* No assignment to %EDX, since we're blocking. */
-      if (VG_(clo_trace_pthread_level) >= 1) {
-         VG_(sprintf)(msg_buf, "pthread_mutex_lock   %d: BLOCK", 
-                               mid );
-         print_pthread_event(tid, msg_buf);
-      }
+
    } else {
-      /* We get it! */
-      vg_mutexes[mid].held  = True;
+      /* We get it! [for the first time]. */
+      vg_mutexes[mid].count = 1;
       vg_mutexes[mid].owner = tid;
       /* return 0 (success). */
       vg_threads[tid].m_edx = 0;
    }
+
 }
 
 
@@ -1612,11 +1642,25 @@ void do_pthread_mutex_unlock ( ThreadId tid,
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    /* Barf if we don't currently hold the mutex. */
-   if (!vg_mutexes[mid].held || vg_mutexes[mid].owner != tid) {
+   if (vg_mutexes[mid].count == 0 /* nobody holds it */
+       || vg_mutexes[mid].owner != tid /* we don't hold it */) {
       vg_threads[tid].m_edx = EPERM;
       return;
    }
 
+   /* If it's a multiply-locked recursive mutex, just decrement the
+      lock count and return. */
+   if (vg_mutexes[mid].count > 1) {
+      vg_assert(vg_mutexes[mid].is_rec);
+      vg_mutexes[mid].count --;
+      vg_threads[tid].m_edx = 0; /* success */
+      return;
+   }
+
+   /* Now we're sure mid is locked exactly once, and by the thread who
+      is now doing an unlock on it.  */
+   vg_assert(vg_mutexes[mid].count == 1);
+
    /* Find some arbitrary thread waiting on this mutex, and make it
       runnable.  If none are waiting, mark the mutex as not held. */
    for (i = 0; i < VG_N_THREADS; i++) {
@@ -1630,10 +1674,11 @@ void do_pthread_mutex_unlock ( ThreadId tid,
    vg_assert(i <= VG_N_THREADS);
    if (i == VG_N_THREADS) {
       /* Nobody else is waiting on it. */
-      vg_mutexes[mid].held = False;
+      vg_mutexes[mid].count = 0;
    } else {
       /* Notionally transfer the hold to thread i, whose
          pthread_mutex_lock() call now returns with 0 (success). */
+      /* The .count is already == 1. */
       vg_mutexes[mid].owner = i;
       vg_threads[i].status = VgTs_Runnable;
       vg_threads[i].m_edx = 0; /* pth_lock() success */
@@ -1679,7 +1724,7 @@ static void do_pthread_mutex_destroy ( ThreadId tid,
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    /* Barf if the mutex is currently held. */
-   if (vg_mutexes[mid].held) {
+   if (vg_mutexes[mid].count > 0) {
       vg_threads[tid].m_edx = EBUSY;
       return;
    }