]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Support for POSIX reader-writer locks (preliminary).
authorJulian Seward <jseward@acm.org>
Mon, 27 May 2002 13:00:05 +0000 (13:00 +0000)
committerJulian Seward <jseward@acm.org>
Mon, 27 May 2002 13:00:05 +0000 (13:00 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@325

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

index d6ebeeada26d306e15ebb29509b32472ddea791b..8d30fcf526e27d4246d75e0f75398e83c9e9823d 100644 (file)
 #include "valgrind.h"    /* For the request-passing mechanism */
 #include "vg_include.h"  /* For the VG_USERREQ__* constants */
 
+#define __USE_UNIX98
+#include <sys/types.h>
+#include <pthread.h>
+#undef __USE_UNIX98
+
 #include <unistd.h>
 #include <string.h>
 #ifdef GLIBC_2_1
@@ -181,12 +186,12 @@ void vgPlain_unimp ( char* what )
    Pass pthread_ calls to Valgrind's request mechanism.
    ------------------------------------------------------------------ */
 
-#include <pthread.h>
 #include <stdio.h>
 #include <errno.h>
 #include <assert.h>
 #include <sys/time.h> /* gettimeofday */
 
+
 /* ---------------------------------------------------
    THREAD ATTRIBUTES
    ------------------------------------------------ */
@@ -1623,7 +1628,7 @@ static vg_sem_t* se_remap ( sem_t* orig )
       if (se_remap_used == VG_N_SEMAPHORES) {
          res = pthread_mutex_unlock(&se_remap_mx);
          assert(res == 0);
-         barf("N_SEMAPHORES is too low.  Increase and recompile.");
+         barf("VG_N_SEMAPHORES is too low.  Increase and recompile.");
       }
       se_remap_used++;
       se_remap_orig[i] = orig;
@@ -1732,6 +1737,496 @@ int sem_destroy(sem_t * sem)
 }
 
 
+/* ---------------------------------------------------------------------
+   Hacky implementation of reader-writer locks.
+   ------------------------------------------------------------------ */
+
+/*
+Errata from 7th printing:
+
+   Page 259, rwlock.c, line 27, the two "!=" should be ">", for
+   consistency with other similar tests. (The values should never be
+   negative; this isn't a fix, but an improvement to clarity and
+   consistency.)  
+
+      [27] if (rwl->r_wait > 0 || rwl->w_wait > 0) {
+
+   Page 259, rwlock.c, lines 39 and 40, in both lines, "==" should
+   become "!=":
+
+      [39] return (status != 0 ? status 
+      [40] : (status1 != 0 ? status1 : status2)); 
+*/
+
+/*
+ * rwlock.h
+ *
+ * This header file describes the "reader/writer lock" synchronization
+ * construct. The type rwlock_t describes the full state of the lock
+ * including the POSIX 1003.1c synchronization objects necessary.
+ *
+ * A reader/writer lock allows a thread to lock shared data either for shared
+ * read access or exclusive write access.
+ *
+ * The rwl_init() and rwl_destroy() functions, respectively, allow you to
+ * initialize/create and destroy/free the reader/writer lock.
+ */
+
+#define VG_N_RWLOCKS 50
+
+/*
+ * Structure describing a read-write lock.
+ */
+typedef struct {
+    pthread_mutex_t     mutex;
+    pthread_cond_t      read;           /* wait for read */
+    pthread_cond_t      write;          /* wait for write */
+    int                 valid;          /* set when valid */
+    int                 r_active;       /* readers active */
+    int                 w_active;       /* writer active */
+    int                 r_wait;         /* readers waiting */
+    int                 w_wait;         /* writers waiting */
+    int                 pref_writer;    /* != 0 --> prefer writer */
+} vg_rwlock_t;
+
+#define VG_RWLOCK_VALID    0xfacade
+
+
+/*
+ * Support static initialization of barriers
+ */
+#define VG_RWL_INITIALIZER \
+    {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
+    PTHREAD_COND_INITIALIZER, VG_RWLOCK_VALID, 0, 0, 0, 0, 1}
+
+
+static pthread_mutex_t rw_remap_mx = PTHREAD_MUTEX_INITIALIZER;
+
+static int                 rw_remap_used = 0;
+static pthread_rwlock_t*   rw_remap_orig[VG_N_RWLOCKS];
+static vg_rwlock_t         rw_remap_new[VG_N_RWLOCKS];
+
+/* Take the address of a LinuxThreads rwlock_t and return the shadow
+   address of our version.  Further, if the LinuxThreads version
+   appears to have been statically initialised, do the same to the one
+   we allocate here.  The pthread_rwlock_t.__rw_readers field is set
+   to zero by PTHREAD_RWLOCK_INITIALIZER, so we take zero as meaning
+   uninitialised and non-zero meaning initialised. 
+*/
+static vg_rwlock_t* rw_remap ( pthread_rwlock_t* orig )
+{
+   int          res, i;
+   vg_rwlock_t* vg_rwl;
+   res = __pthread_mutex_lock(&rw_remap_mx);
+   assert(res == 0);
+
+   for (i = 0; i < rw_remap_used; i++) {
+      if (rw_remap_orig[i] == orig)
+         break;
+   }
+   if (i == rw_remap_used) {
+      if (rw_remap_used == VG_N_RWLOCKS) {
+         res = pthread_mutex_unlock(&rw_remap_mx);
+         assert(res == 0);
+         barf("VG_N_RWLOCKS is too low.  Increase and recompile.");
+      }
+      rw_remap_used++;
+      rw_remap_orig[i] = orig;
+      if (0) printf("allocated rwlock %d\n", i);
+   }
+   res = __pthread_mutex_unlock(&rw_remap_mx);
+   assert(res == 0);
+   vg_rwl = &rw_remap_new[i];
+
+   /* Mimic static initialisation of the original. */
+   if (orig->__rw_readers == 0) {
+      const vg_rwlock_t default_rwl = VG_RWL_INITIALIZER;
+      orig->__rw_readers = 1;
+      *vg_rwl = default_rwl;
+      vg_rwl->pref_writer = 1;
+      if (orig->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
+         vg_rwl->pref_writer = 0;
+   }
+
+   return vg_rwl;
+}
+
+
+/*
+ * rwlock.c
+ *
+ * This file implements the "read-write lock" synchronization
+ * construct.
+ *
+ * A read-write lock allows a thread to lock shared data either
+ * for shared read access or exclusive write access.
+ *
+ * The rwl_init() and rwl_destroy() functions, respectively,
+ * allow you to initialize/create and destroy/free the
+ * read-write lock.
+ *
+ * The rwl_readlock() function locks a read-write lock for
+ * shared read access, and rwl_readunlock() releases the
+ * lock. rwl_readtrylock() attempts to lock a read-write lock
+ * for read access, and returns EBUSY instead of blocking.
+ *
+ * The rwl_writelock() function locks a read-write lock for
+ * exclusive write access, and rwl_writeunlock() releases the
+ * lock. rwl_writetrylock() attempts to lock a read-write lock
+ * for write access, and returns EBUSY instead of blocking.
+ */
+
+
+/*
+ * Initialize a read-write lock
+ */
+static int rwl_init ( vg_rwlock_t *rwl )
+{
+    int status;
+
+    rwl->r_active = 0;
+    rwl->r_wait = rwl->w_wait = 0;
+    rwl->w_active = 0;
+    status = pthread_mutex_init (&rwl->mutex, NULL);
+    if (status != 0)
+        return status;
+    status = pthread_cond_init (&rwl->read, NULL);
+    if (status != 0) {
+        /* if unable to create read CV, destroy mutex */
+        pthread_mutex_destroy (&rwl->mutex);
+        return status;
+    }
+    status = pthread_cond_init (&rwl->write, NULL);
+    if (status != 0) {
+        /* if unable to create write CV, destroy read CV and mutex */
+        pthread_cond_destroy (&rwl->read);
+        pthread_mutex_destroy (&rwl->mutex);
+        return status;
+    }
+    rwl->valid = VG_RWLOCK_VALID;
+    return 0;
+}
+
+/*
+ * Destroy a read-write lock
+ */
+static int rwl_destroy (vg_rwlock_t *rwl)
+{
+    int status, status1, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+
+    /*
+     * Check whether any threads own the lock; report "BUSY" if
+     * so.
+     */
+    if (rwl->r_active > 0 || rwl->w_active) {
+        pthread_mutex_unlock (&rwl->mutex);
+        return EBUSY;
+    }
+
+    /*
+     * Check whether any threads are known to be waiting; report
+     * EBUSY if so.
+     */
+    if (rwl->r_wait > 0 || rwl->w_wait > 0) {
+        pthread_mutex_unlock (&rwl->mutex);
+        return EBUSY;
+    }
+
+    rwl->valid = 0;
+    status = pthread_mutex_unlock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    status = pthread_mutex_destroy (&rwl->mutex);
+    status1 = pthread_cond_destroy (&rwl->read);
+    status2 = pthread_cond_destroy (&rwl->write);
+    return (status != 0 ? status : (status1 != 0 ? status1 : status2));
+}
+
+/*
+ * Handle cleanup when the read lock condition variable
+ * wait is cancelled.
+ *
+ * Simply record that the thread is no longer waiting,
+ * and unlock the mutex.
+ */
+static void rwl_readcleanup (void *arg)
+{
+    vg_rwlock_t    *rwl = (vg_rwlock_t *)arg;
+
+    rwl->r_wait--;
+    pthread_mutex_unlock (&rwl->mutex);
+}
+
+/*
+ * Lock a read-write lock for read access.
+ */
+static int rwl_readlock (vg_rwlock_t *rwl)
+{
+    int status;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active) {
+        rwl->r_wait++;
+        pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
+        while (rwl->w_active) {
+            status = pthread_cond_wait (&rwl->read, &rwl->mutex);
+            if (status != 0)
+                break;
+        }
+        pthread_cleanup_pop (0);
+        rwl->r_wait--;
+    }
+    if (status == 0)
+        rwl->r_active++;
+    pthread_mutex_unlock (&rwl->mutex);
+    return status;
+}
+
+/*
+ * Attempt to lock a read-write lock for read access (don't
+ * block if unavailable).
+ */
+static int rwl_readtrylock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active)
+        status = EBUSY;
+    else
+        rwl->r_active++;
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status2 != 0 ? status2 : status);
+}
+
+/*
+ * Handle cleanup when the write lock condition variable
+ * wait is cancelled.
+ *
+ * Simply record that the thread is no longer waiting,
+ * and unlock the mutex.
+ */
+static void rwl_writecleanup (void *arg)
+{
+    vg_rwlock_t *rwl = (vg_rwlock_t *)arg;
+
+    rwl->w_wait--;
+    pthread_mutex_unlock (&rwl->mutex);
+}
+
+/*
+ * Lock a read-write lock for write access.
+ */
+static int rwl_writelock (vg_rwlock_t *rwl)
+{
+    int status;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active || rwl->r_active > 0) {
+        rwl->w_wait++;
+        pthread_cleanup_push (rwl_writecleanup, (void*)rwl);
+        while (rwl->w_active || rwl->r_active > 0) {
+            status = pthread_cond_wait (&rwl->write, &rwl->mutex);
+            if (status != 0)
+                break;
+        }
+        pthread_cleanup_pop (0);
+        rwl->w_wait--;
+    }
+    if (status == 0)
+        rwl->w_active = 1;
+    pthread_mutex_unlock (&rwl->mutex);
+    return status;
+}
+
+/*
+ * Attempt to lock a read-write lock for write access. Don't
+ * block if unavailable.
+ */
+static int rwl_writetrylock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active || rwl->r_active > 0)
+        status = EBUSY;
+    else
+        rwl->w_active = 1;
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status != 0 ? status : status2);
+}
+
+/*
+ * Unlock a read-write lock, using the r_active and w_active fields to
+ * decide whether we're in a read or write lock.
+ */
+static int rwl_unlock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+
+    if (rwl->r_active > 0) {
+
+       /* READ case */
+       assert(!rwl->w_active);
+       rwl->r_active--;
+       if (rwl->r_active == 0 && rwl->w_wait > 0)
+          status = pthread_cond_signal (&rwl->write);
+       /* END READ case */
+
+    } else {
+
+       /* WRITE case */
+       assert(rwl->w_active);
+       assert(rwl->r_active == 0);
+       rwl->w_active = 0;
+
+       if (rwl->pref_writer) {
+          /* Do writer-preference wakeups. */
+          if (rwl->w_wait > 0) {
+             status = pthread_cond_signal (&rwl->write);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          } else if (rwl->r_wait > 0) {
+             status = pthread_cond_broadcast (&rwl->read);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+         }
+       } else {
+          /* Do reader-preference wakeups. */
+          if (rwl->r_wait > 0) {
+             status = pthread_cond_broadcast (&rwl->read);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          } else if (rwl->w_wait > 0) {
+             status = pthread_cond_signal (&rwl->write);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          }
+       }
+       /* END WRITE case */
+
+    }
+
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status2 == 0 ? status : status2);
+}
+
+/* -------------------------------- */
+
+int pthread_rwlock_init ( pthread_rwlock_t* orig,
+                          const pthread_rwlockattr_t* attr )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_init\n");
+   /* Force the remapper to initialise the shadow. */
+   orig->__rw_readers = 0;
+   /* Install the lock preference; the remapper needs to know it. */
+   orig->__rw_kind = PTHREAD_RWLOCK_DEFAULT_NP;
+   if (attr)
+      orig->__rw_kind = attr->__lockkind;
+   rwl = rw_remap ( orig );
+   res = rwl_init ( rwl );
+   return res;
+}
+
+int pthread_rwlock_destroy ( pthread_rwlock_t *orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_destroy\n");
+   rwl = rw_remap ( orig );
+   res = rwl_destroy ( rwl );
+   return res;
+}
+
+int pthread_rwlock_rdlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_rdlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_readlock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_tryrdlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_tryrdlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_readtrylock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_wrlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_wrlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_writelock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_trywrlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_writetrylock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_unlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_unlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_unlock ( rwl );
+   return res;
+}
+
+
 /* ---------------------------------------------------------------------
    B'stard.
    ------------------------------------------------------------------ */
@@ -1779,41 +2274,15 @@ weak_alias(__fork, fork)
 
 /*--------------------------------------------------*/
 
-int
-pthread_rwlock_rdlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_rdlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_rdlock, __pthread_rwlock_rdlock)
-
-
-int
-pthread_rwlock_unlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_unlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_unlock, __pthread_rwlock_unlock)
-
-
-int
-pthread_rwlock_wrlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_wrlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock)
 
+weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
+weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
+weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
+weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
+
 
 /* I've no idea what these are, but they get called quite a lot.
    Anybody know? */
index 16b251ecbb0affcc1cf8a426651fc298ac6f447f..de92468019ecc14d72349f82949552c10b5f4389 100644 (file)
@@ -130,13 +130,13 @@ void pthread_mutex_timedlock ( void )  { unimp("pthread_mutex_timedlock"); }
 //void pthread_mutexattr_destroy ( void )  { unimp("pthread_mutexattr_destroy"); }
 //void pthread_mutexattr_init ( void )  { unimp("pthread_mutexattr_init"); }
 //void pthread_once ( void )  { unimp("pthread_once"); }
-void pthread_rwlock_destroy ( void )  { unimp("pthread_rwlock_destroy"); }
-void pthread_rwlock_init ( void )  { unimp("pthread_rwlock_init"); }
+//void pthread_rwlock_destroy ( void )  { unimp("pthread_rwlock_destroy"); }
+//void pthread_rwlock_init ( void )  { unimp("pthread_rwlock_init"); }
 //void pthread_rwlock_rdlock ( void )  { unimp("pthread_rwlock_rdlock"); }
 void pthread_rwlock_timedrdlock ( void )  { unimp("pthread_rwlock_timedrdlock"); }
 void pthread_rwlock_timedwrlock ( void )  { unimp("pthread_rwlock_timedwrlock"); }
-void pthread_rwlock_tryrdlock ( void )  { unimp("pthread_rwlock_tryrdlock"); }
-void pthread_rwlock_trywrlock ( void )  { unimp("pthread_rwlock_trywrlock"); }
+//void pthread_rwlock_tryrdlock ( void )  { unimp("pthread_rwlock_tryrdlock"); }
+//void pthread_rwlock_trywrlock ( void )  { unimp("pthread_rwlock_trywrlock"); }
 //void pthread_rwlock_unlock ( void )  { unimp("pthread_rwlock_unlock"); }
 //void pthread_rwlock_wrlock ( void )  { unimp("pthread_rwlock_wrlock"); }
 void pthread_rwlockattr_destroy ( void )  { unimp("pthread_rwlockattr_destroy"); }
@@ -193,10 +193,10 @@ void pthread_attr_init@GLIBC_2.0 ( void )  { unimp("pthread_attr_init@GLIBC_2.0"
 # define weak_alias(name, aliasname) \
   extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
 
-weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
-weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
-weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
-weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
+//weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
+//weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
+//weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
+//weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
 //weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock)
 weak_alias(_IO_ftrylockfile, ftrylockfile)
 
index d6ebeeada26d306e15ebb29509b32472ddea791b..8d30fcf526e27d4246d75e0f75398e83c9e9823d 100644 (file)
 #include "valgrind.h"    /* For the request-passing mechanism */
 #include "vg_include.h"  /* For the VG_USERREQ__* constants */
 
+#define __USE_UNIX98
+#include <sys/types.h>
+#include <pthread.h>
+#undef __USE_UNIX98
+
 #include <unistd.h>
 #include <string.h>
 #ifdef GLIBC_2_1
@@ -181,12 +186,12 @@ void vgPlain_unimp ( char* what )
    Pass pthread_ calls to Valgrind's request mechanism.
    ------------------------------------------------------------------ */
 
-#include <pthread.h>
 #include <stdio.h>
 #include <errno.h>
 #include <assert.h>
 #include <sys/time.h> /* gettimeofday */
 
+
 /* ---------------------------------------------------
    THREAD ATTRIBUTES
    ------------------------------------------------ */
@@ -1623,7 +1628,7 @@ static vg_sem_t* se_remap ( sem_t* orig )
       if (se_remap_used == VG_N_SEMAPHORES) {
          res = pthread_mutex_unlock(&se_remap_mx);
          assert(res == 0);
-         barf("N_SEMAPHORES is too low.  Increase and recompile.");
+         barf("VG_N_SEMAPHORES is too low.  Increase and recompile.");
       }
       se_remap_used++;
       se_remap_orig[i] = orig;
@@ -1732,6 +1737,496 @@ int sem_destroy(sem_t * sem)
 }
 
 
+/* ---------------------------------------------------------------------
+   Hacky implementation of reader-writer locks.
+   ------------------------------------------------------------------ */
+
+/*
+Errata from 7th printing:
+
+   Page 259, rwlock.c, line 27, the two "!=" should be ">", for
+   consistency with other similar tests. (The values should never be
+   negative; this isn't a fix, but an improvement to clarity and
+   consistency.)  
+
+      [27] if (rwl->r_wait > 0 || rwl->w_wait > 0) {
+
+   Page 259, rwlock.c, lines 39 and 40, in both lines, "==" should
+   become "!=":
+
+      [39] return (status != 0 ? status 
+      [40] : (status1 != 0 ? status1 : status2)); 
+*/
+
+/*
+ * rwlock.h
+ *
+ * This header file describes the "reader/writer lock" synchronization
+ * construct. The type rwlock_t describes the full state of the lock
+ * including the POSIX 1003.1c synchronization objects necessary.
+ *
+ * A reader/writer lock allows a thread to lock shared data either for shared
+ * read access or exclusive write access.
+ *
+ * The rwl_init() and rwl_destroy() functions, respectively, allow you to
+ * initialize/create and destroy/free the reader/writer lock.
+ */
+
+#define VG_N_RWLOCKS 50
+
+/*
+ * Structure describing a read-write lock.
+ */
+typedef struct {
+    pthread_mutex_t     mutex;
+    pthread_cond_t      read;           /* wait for read */
+    pthread_cond_t      write;          /* wait for write */
+    int                 valid;          /* set when valid */
+    int                 r_active;       /* readers active */
+    int                 w_active;       /* writer active */
+    int                 r_wait;         /* readers waiting */
+    int                 w_wait;         /* writers waiting */
+    int                 pref_writer;    /* != 0 --> prefer writer */
+} vg_rwlock_t;
+
+#define VG_RWLOCK_VALID    0xfacade
+
+
+/*
+ * Support static initialization of barriers
+ */
+#define VG_RWL_INITIALIZER \
+    {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
+    PTHREAD_COND_INITIALIZER, VG_RWLOCK_VALID, 0, 0, 0, 0, 1}
+
+
+static pthread_mutex_t rw_remap_mx = PTHREAD_MUTEX_INITIALIZER;
+
+static int                 rw_remap_used = 0;
+static pthread_rwlock_t*   rw_remap_orig[VG_N_RWLOCKS];
+static vg_rwlock_t         rw_remap_new[VG_N_RWLOCKS];
+
+/* Take the address of a LinuxThreads rwlock_t and return the shadow
+   address of our version.  Further, if the LinuxThreads version
+   appears to have been statically initialised, do the same to the one
+   we allocate here.  The pthread_rwlock_t.__rw_readers field is set
+   to zero by PTHREAD_RWLOCK_INITIALIZER, so we take zero as meaning
+   uninitialised and non-zero meaning initialised. 
+*/
+static vg_rwlock_t* rw_remap ( pthread_rwlock_t* orig )
+{
+   int          res, i;
+   vg_rwlock_t* vg_rwl;
+   res = __pthread_mutex_lock(&rw_remap_mx);
+   assert(res == 0);
+
+   for (i = 0; i < rw_remap_used; i++) {
+      if (rw_remap_orig[i] == orig)
+         break;
+   }
+   if (i == rw_remap_used) {
+      if (rw_remap_used == VG_N_RWLOCKS) {
+         res = pthread_mutex_unlock(&rw_remap_mx);
+         assert(res == 0);
+         barf("VG_N_RWLOCKS is too low.  Increase and recompile.");
+      }
+      rw_remap_used++;
+      rw_remap_orig[i] = orig;
+      if (0) printf("allocated rwlock %d\n", i);
+   }
+   res = __pthread_mutex_unlock(&rw_remap_mx);
+   assert(res == 0);
+   vg_rwl = &rw_remap_new[i];
+
+   /* Mimic static initialisation of the original. */
+   if (orig->__rw_readers == 0) {
+      const vg_rwlock_t default_rwl = VG_RWL_INITIALIZER;
+      orig->__rw_readers = 1;
+      *vg_rwl = default_rwl;
+      vg_rwl->pref_writer = 1;
+      if (orig->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
+         vg_rwl->pref_writer = 0;
+   }
+
+   return vg_rwl;
+}
+
+
+/*
+ * rwlock.c
+ *
+ * This file implements the "read-write lock" synchronization
+ * construct.
+ *
+ * A read-write lock allows a thread to lock shared data either
+ * for shared read access or exclusive write access.
+ *
+ * The rwl_init() and rwl_destroy() functions, respectively,
+ * allow you to initialize/create and destroy/free the
+ * read-write lock.
+ *
+ * The rwl_readlock() function locks a read-write lock for
+ * shared read access, and rwl_readunlock() releases the
+ * lock. rwl_readtrylock() attempts to lock a read-write lock
+ * for read access, and returns EBUSY instead of blocking.
+ *
+ * The rwl_writelock() function locks a read-write lock for
+ * exclusive write access, and rwl_writeunlock() releases the
+ * lock. rwl_writetrylock() attempts to lock a read-write lock
+ * for write access, and returns EBUSY instead of blocking.
+ */
+
+
+/*
+ * Initialize a read-write lock
+ */
+static int rwl_init ( vg_rwlock_t *rwl )
+{
+    int status;
+
+    rwl->r_active = 0;
+    rwl->r_wait = rwl->w_wait = 0;
+    rwl->w_active = 0;
+    status = pthread_mutex_init (&rwl->mutex, NULL);
+    if (status != 0)
+        return status;
+    status = pthread_cond_init (&rwl->read, NULL);
+    if (status != 0) {
+        /* if unable to create read CV, destroy mutex */
+        pthread_mutex_destroy (&rwl->mutex);
+        return status;
+    }
+    status = pthread_cond_init (&rwl->write, NULL);
+    if (status != 0) {
+        /* if unable to create write CV, destroy read CV and mutex */
+        pthread_cond_destroy (&rwl->read);
+        pthread_mutex_destroy (&rwl->mutex);
+        return status;
+    }
+    rwl->valid = VG_RWLOCK_VALID;
+    return 0;
+}
+
+/*
+ * Destroy a read-write lock
+ */
+static int rwl_destroy (vg_rwlock_t *rwl)
+{
+    int status, status1, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+
+    /*
+     * Check whether any threads own the lock; report "BUSY" if
+     * so.
+     */
+    if (rwl->r_active > 0 || rwl->w_active) {
+        pthread_mutex_unlock (&rwl->mutex);
+        return EBUSY;
+    }
+
+    /*
+     * Check whether any threads are known to be waiting; report
+     * EBUSY if so.
+     */
+    if (rwl->r_wait > 0 || rwl->w_wait > 0) {
+        pthread_mutex_unlock (&rwl->mutex);
+        return EBUSY;
+    }
+
+    rwl->valid = 0;
+    status = pthread_mutex_unlock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    status = pthread_mutex_destroy (&rwl->mutex);
+    status1 = pthread_cond_destroy (&rwl->read);
+    status2 = pthread_cond_destroy (&rwl->write);
+    return (status != 0 ? status : (status1 != 0 ? status1 : status2));
+}
+
+/*
+ * Handle cleanup when the read lock condition variable
+ * wait is cancelled.
+ *
+ * Simply record that the thread is no longer waiting,
+ * and unlock the mutex.
+ */
+static void rwl_readcleanup (void *arg)
+{
+    vg_rwlock_t    *rwl = (vg_rwlock_t *)arg;
+
+    rwl->r_wait--;
+    pthread_mutex_unlock (&rwl->mutex);
+}
+
+/*
+ * Lock a read-write lock for read access.
+ */
+static int rwl_readlock (vg_rwlock_t *rwl)
+{
+    int status;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active) {
+        rwl->r_wait++;
+        pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
+        while (rwl->w_active) {
+            status = pthread_cond_wait (&rwl->read, &rwl->mutex);
+            if (status != 0)
+                break;
+        }
+        pthread_cleanup_pop (0);
+        rwl->r_wait--;
+    }
+    if (status == 0)
+        rwl->r_active++;
+    pthread_mutex_unlock (&rwl->mutex);
+    return status;
+}
+
+/*
+ * Attempt to lock a read-write lock for read access (don't
+ * block if unavailable).
+ */
+static int rwl_readtrylock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active)
+        status = EBUSY;
+    else
+        rwl->r_active++;
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status2 != 0 ? status2 : status);
+}
+
+/*
+ * Handle cleanup when the write lock condition variable
+ * wait is cancelled.
+ *
+ * Simply record that the thread is no longer waiting,
+ * and unlock the mutex.
+ */
+static void rwl_writecleanup (void *arg)
+{
+    vg_rwlock_t *rwl = (vg_rwlock_t *)arg;
+
+    rwl->w_wait--;
+    pthread_mutex_unlock (&rwl->mutex);
+}
+
+/*
+ * Lock a read-write lock for write access.
+ */
+static int rwl_writelock (vg_rwlock_t *rwl)
+{
+    int status;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active || rwl->r_active > 0) {
+        rwl->w_wait++;
+        pthread_cleanup_push (rwl_writecleanup, (void*)rwl);
+        while (rwl->w_active || rwl->r_active > 0) {
+            status = pthread_cond_wait (&rwl->write, &rwl->mutex);
+            if (status != 0)
+                break;
+        }
+        pthread_cleanup_pop (0);
+        rwl->w_wait--;
+    }
+    if (status == 0)
+        rwl->w_active = 1;
+    pthread_mutex_unlock (&rwl->mutex);
+    return status;
+}
+
+/*
+ * Attempt to lock a read-write lock for write access. Don't
+ * block if unavailable.
+ */
+static int rwl_writetrylock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active || rwl->r_active > 0)
+        status = EBUSY;
+    else
+        rwl->w_active = 1;
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status != 0 ? status : status2);
+}
+
+/*
+ * Unlock a read-write lock, using the r_active and w_active fields to
+ * decide whether we're in a read or write lock.
+ */
+static int rwl_unlock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+
+    if (rwl->r_active > 0) {
+
+       /* READ case */
+       assert(!rwl->w_active);
+       rwl->r_active--;
+       if (rwl->r_active == 0 && rwl->w_wait > 0)
+          status = pthread_cond_signal (&rwl->write);
+       /* END READ case */
+
+    } else {
+
+       /* WRITE case */
+       assert(rwl->w_active);
+       assert(rwl->r_active == 0);
+       rwl->w_active = 0;
+
+       if (rwl->pref_writer) {
+          /* Do writer-preference wakeups. */
+          if (rwl->w_wait > 0) {
+             status = pthread_cond_signal (&rwl->write);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          } else if (rwl->r_wait > 0) {
+             status = pthread_cond_broadcast (&rwl->read);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+         }
+       } else {
+          /* Do reader-preference wakeups. */
+          if (rwl->r_wait > 0) {
+             status = pthread_cond_broadcast (&rwl->read);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          } else if (rwl->w_wait > 0) {
+             status = pthread_cond_signal (&rwl->write);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          }
+       }
+       /* END WRITE case */
+
+    }
+
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status2 == 0 ? status : status2);
+}
+
+/* -------------------------------- */
+
+int pthread_rwlock_init ( pthread_rwlock_t* orig,
+                          const pthread_rwlockattr_t* attr )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_init\n");
+   /* Force the remapper to initialise the shadow. */
+   orig->__rw_readers = 0;
+   /* Install the lock preference; the remapper needs to know it. */
+   orig->__rw_kind = PTHREAD_RWLOCK_DEFAULT_NP;
+   if (attr)
+      orig->__rw_kind = attr->__lockkind;
+   rwl = rw_remap ( orig );
+   res = rwl_init ( rwl );
+   return res;
+}
+
+int pthread_rwlock_destroy ( pthread_rwlock_t *orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_destroy\n");
+   rwl = rw_remap ( orig );
+   res = rwl_destroy ( rwl );
+   return res;
+}
+
+int pthread_rwlock_rdlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_rdlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_readlock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_tryrdlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_tryrdlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_readtrylock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_wrlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_wrlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_writelock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_trywrlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_writetrylock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_unlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_unlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_unlock ( rwl );
+   return res;
+}
+
+
 /* ---------------------------------------------------------------------
    B'stard.
    ------------------------------------------------------------------ */
@@ -1779,41 +2274,15 @@ weak_alias(__fork, fork)
 
 /*--------------------------------------------------*/
 
-int
-pthread_rwlock_rdlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_rdlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_rdlock, __pthread_rwlock_rdlock)
-
-
-int
-pthread_rwlock_unlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_unlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_unlock, __pthread_rwlock_unlock)
-
-
-int
-pthread_rwlock_wrlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_wrlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock)
 
+weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
+weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
+weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
+weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
+
 
 /* I've no idea what these are, but they get called quite a lot.
    Anybody know? */
index 16b251ecbb0affcc1cf8a426651fc298ac6f447f..de92468019ecc14d72349f82949552c10b5f4389 100644 (file)
@@ -130,13 +130,13 @@ void pthread_mutex_timedlock ( void )  { unimp("pthread_mutex_timedlock"); }
 //void pthread_mutexattr_destroy ( void )  { unimp("pthread_mutexattr_destroy"); }
 //void pthread_mutexattr_init ( void )  { unimp("pthread_mutexattr_init"); }
 //void pthread_once ( void )  { unimp("pthread_once"); }
-void pthread_rwlock_destroy ( void )  { unimp("pthread_rwlock_destroy"); }
-void pthread_rwlock_init ( void )  { unimp("pthread_rwlock_init"); }
+//void pthread_rwlock_destroy ( void )  { unimp("pthread_rwlock_destroy"); }
+//void pthread_rwlock_init ( void )  { unimp("pthread_rwlock_init"); }
 //void pthread_rwlock_rdlock ( void )  { unimp("pthread_rwlock_rdlock"); }
 void pthread_rwlock_timedrdlock ( void )  { unimp("pthread_rwlock_timedrdlock"); }
 void pthread_rwlock_timedwrlock ( void )  { unimp("pthread_rwlock_timedwrlock"); }
-void pthread_rwlock_tryrdlock ( void )  { unimp("pthread_rwlock_tryrdlock"); }
-void pthread_rwlock_trywrlock ( void )  { unimp("pthread_rwlock_trywrlock"); }
+//void pthread_rwlock_tryrdlock ( void )  { unimp("pthread_rwlock_tryrdlock"); }
+//void pthread_rwlock_trywrlock ( void )  { unimp("pthread_rwlock_trywrlock"); }
 //void pthread_rwlock_unlock ( void )  { unimp("pthread_rwlock_unlock"); }
 //void pthread_rwlock_wrlock ( void )  { unimp("pthread_rwlock_wrlock"); }
 void pthread_rwlockattr_destroy ( void )  { unimp("pthread_rwlockattr_destroy"); }
@@ -193,10 +193,10 @@ void pthread_attr_init@GLIBC_2.0 ( void )  { unimp("pthread_attr_init@GLIBC_2.0"
 # define weak_alias(name, aliasname) \
   extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
 
-weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
-weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
-weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
-weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
+//weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
+//weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
+//weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
+//weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
 //weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock)
 weak_alias(_IO_ftrylockfile, ftrylockfile)
 
index d6ebeeada26d306e15ebb29509b32472ddea791b..8d30fcf526e27d4246d75e0f75398e83c9e9823d 100644 (file)
 #include "valgrind.h"    /* For the request-passing mechanism */
 #include "vg_include.h"  /* For the VG_USERREQ__* constants */
 
+#define __USE_UNIX98
+#include <sys/types.h>
+#include <pthread.h>
+#undef __USE_UNIX98
+
 #include <unistd.h>
 #include <string.h>
 #ifdef GLIBC_2_1
@@ -181,12 +186,12 @@ void vgPlain_unimp ( char* what )
    Pass pthread_ calls to Valgrind's request mechanism.
    ------------------------------------------------------------------ */
 
-#include <pthread.h>
 #include <stdio.h>
 #include <errno.h>
 #include <assert.h>
 #include <sys/time.h> /* gettimeofday */
 
+
 /* ---------------------------------------------------
    THREAD ATTRIBUTES
    ------------------------------------------------ */
@@ -1623,7 +1628,7 @@ static vg_sem_t* se_remap ( sem_t* orig )
       if (se_remap_used == VG_N_SEMAPHORES) {
          res = pthread_mutex_unlock(&se_remap_mx);
          assert(res == 0);
-         barf("N_SEMAPHORES is too low.  Increase and recompile.");
+         barf("VG_N_SEMAPHORES is too low.  Increase and recompile.");
       }
       se_remap_used++;
       se_remap_orig[i] = orig;
@@ -1732,6 +1737,496 @@ int sem_destroy(sem_t * sem)
 }
 
 
+/* ---------------------------------------------------------------------
+   Hacky implementation of reader-writer locks.
+   ------------------------------------------------------------------ */
+
+/*
+Errata from 7th printing:
+
+   Page 259, rwlock.c, line 27, the two "!=" should be ">", for
+   consistency with other similar tests. (The values should never be
+   negative; this isn't a fix, but an improvement to clarity and
+   consistency.)  
+
+      [27] if (rwl->r_wait > 0 || rwl->w_wait > 0) {
+
+   Page 259, rwlock.c, lines 39 and 40, in both lines, "==" should
+   become "!=":
+
+      [39] return (status != 0 ? status 
+      [40] : (status1 != 0 ? status1 : status2)); 
+*/
+
+/*
+ * rwlock.h
+ *
+ * This header file describes the "reader/writer lock" synchronization
+ * construct. The type rwlock_t describes the full state of the lock
+ * including the POSIX 1003.1c synchronization objects necessary.
+ *
+ * A reader/writer lock allows a thread to lock shared data either for shared
+ * read access or exclusive write access.
+ *
+ * The rwl_init() and rwl_destroy() functions, respectively, allow you to
+ * initialize/create and destroy/free the reader/writer lock.
+ */
+
+#define VG_N_RWLOCKS 50
+
+/*
+ * Structure describing a read-write lock.
+ */
+typedef struct {
+    pthread_mutex_t     mutex;
+    pthread_cond_t      read;           /* wait for read */
+    pthread_cond_t      write;          /* wait for write */
+    int                 valid;          /* set when valid */
+    int                 r_active;       /* readers active */
+    int                 w_active;       /* writer active */
+    int                 r_wait;         /* readers waiting */
+    int                 w_wait;         /* writers waiting */
+    int                 pref_writer;    /* != 0 --> prefer writer */
+} vg_rwlock_t;
+
+#define VG_RWLOCK_VALID    0xfacade
+
+
+/*
+ * Support static initialization of barriers
+ */
+#define VG_RWL_INITIALIZER \
+    {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
+    PTHREAD_COND_INITIALIZER, VG_RWLOCK_VALID, 0, 0, 0, 0, 1}
+
+
+static pthread_mutex_t rw_remap_mx = PTHREAD_MUTEX_INITIALIZER;
+
+static int                 rw_remap_used = 0;
+static pthread_rwlock_t*   rw_remap_orig[VG_N_RWLOCKS];
+static vg_rwlock_t         rw_remap_new[VG_N_RWLOCKS];
+
+/* Take the address of a LinuxThreads rwlock_t and return the shadow
+   address of our version.  Further, if the LinuxThreads version
+   appears to have been statically initialised, do the same to the one
+   we allocate here.  The pthread_rwlock_t.__rw_readers field is set
+   to zero by PTHREAD_RWLOCK_INITIALIZER, so we take zero as meaning
+   uninitialised and non-zero meaning initialised. 
+*/
+static vg_rwlock_t* rw_remap ( pthread_rwlock_t* orig )
+{
+   int          res, i;
+   vg_rwlock_t* vg_rwl;
+   res = __pthread_mutex_lock(&rw_remap_mx);
+   assert(res == 0);
+
+   for (i = 0; i < rw_remap_used; i++) {
+      if (rw_remap_orig[i] == orig)
+         break;
+   }
+   if (i == rw_remap_used) {
+      if (rw_remap_used == VG_N_RWLOCKS) {
+         res = pthread_mutex_unlock(&rw_remap_mx);
+         assert(res == 0);
+         barf("VG_N_RWLOCKS is too low.  Increase and recompile.");
+      }
+      rw_remap_used++;
+      rw_remap_orig[i] = orig;
+      if (0) printf("allocated rwlock %d\n", i);
+   }
+   res = __pthread_mutex_unlock(&rw_remap_mx);
+   assert(res == 0);
+   vg_rwl = &rw_remap_new[i];
+
+   /* Mimic static initialisation of the original. */
+   if (orig->__rw_readers == 0) {
+      const vg_rwlock_t default_rwl = VG_RWL_INITIALIZER;
+      orig->__rw_readers = 1;
+      *vg_rwl = default_rwl;
+      vg_rwl->pref_writer = 1;
+      if (orig->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
+         vg_rwl->pref_writer = 0;
+   }
+
+   return vg_rwl;
+}
+
+
+/*
+ * rwlock.c
+ *
+ * This file implements the "read-write lock" synchronization
+ * construct.
+ *
+ * A read-write lock allows a thread to lock shared data either
+ * for shared read access or exclusive write access.
+ *
+ * The rwl_init() and rwl_destroy() functions, respectively,
+ * allow you to initialize/create and destroy/free the
+ * read-write lock.
+ *
+ * The rwl_readlock() function locks a read-write lock for
+ * shared read access, and rwl_readunlock() releases the
+ * lock. rwl_readtrylock() attempts to lock a read-write lock
+ * for read access, and returns EBUSY instead of blocking.
+ *
+ * The rwl_writelock() function locks a read-write lock for
+ * exclusive write access, and rwl_writeunlock() releases the
+ * lock. rwl_writetrylock() attempts to lock a read-write lock
+ * for write access, and returns EBUSY instead of blocking.
+ */
+
+
+/*
+ * Initialize a read-write lock
+ */
+static int rwl_init ( vg_rwlock_t *rwl )
+{
+    int status;
+
+    rwl->r_active = 0;
+    rwl->r_wait = rwl->w_wait = 0;
+    rwl->w_active = 0;
+    status = pthread_mutex_init (&rwl->mutex, NULL);
+    if (status != 0)
+        return status;
+    status = pthread_cond_init (&rwl->read, NULL);
+    if (status != 0) {
+        /* if unable to create read CV, destroy mutex */
+        pthread_mutex_destroy (&rwl->mutex);
+        return status;
+    }
+    status = pthread_cond_init (&rwl->write, NULL);
+    if (status != 0) {
+        /* if unable to create write CV, destroy read CV and mutex */
+        pthread_cond_destroy (&rwl->read);
+        pthread_mutex_destroy (&rwl->mutex);
+        return status;
+    }
+    rwl->valid = VG_RWLOCK_VALID;
+    return 0;
+}
+
+/*
+ * Destroy a read-write lock
+ */
+static int rwl_destroy (vg_rwlock_t *rwl)
+{
+    int status, status1, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+
+    /*
+     * Check whether any threads own the lock; report "BUSY" if
+     * so.
+     */
+    if (rwl->r_active > 0 || rwl->w_active) {
+        pthread_mutex_unlock (&rwl->mutex);
+        return EBUSY;
+    }
+
+    /*
+     * Check whether any threads are known to be waiting; report
+     * EBUSY if so.
+     */
+    if (rwl->r_wait > 0 || rwl->w_wait > 0) {
+        pthread_mutex_unlock (&rwl->mutex);
+        return EBUSY;
+    }
+
+    rwl->valid = 0;
+    status = pthread_mutex_unlock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    status = pthread_mutex_destroy (&rwl->mutex);
+    status1 = pthread_cond_destroy (&rwl->read);
+    status2 = pthread_cond_destroy (&rwl->write);
+    return (status != 0 ? status : (status1 != 0 ? status1 : status2));
+}
+
+/*
+ * Handle cleanup when the read lock condition variable
+ * wait is cancelled.
+ *
+ * Simply record that the thread is no longer waiting,
+ * and unlock the mutex.
+ */
+static void rwl_readcleanup (void *arg)
+{
+    vg_rwlock_t    *rwl = (vg_rwlock_t *)arg;
+
+    rwl->r_wait--;
+    pthread_mutex_unlock (&rwl->mutex);
+}
+
+/*
+ * Lock a read-write lock for read access.
+ */
+static int rwl_readlock (vg_rwlock_t *rwl)
+{
+    int status;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active) {
+        rwl->r_wait++;
+        pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
+        while (rwl->w_active) {
+            status = pthread_cond_wait (&rwl->read, &rwl->mutex);
+            if (status != 0)
+                break;
+        }
+        pthread_cleanup_pop (0);
+        rwl->r_wait--;
+    }
+    if (status == 0)
+        rwl->r_active++;
+    pthread_mutex_unlock (&rwl->mutex);
+    return status;
+}
+
+/*
+ * Attempt to lock a read-write lock for read access (don't
+ * block if unavailable).
+ */
+static int rwl_readtrylock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active)
+        status = EBUSY;
+    else
+        rwl->r_active++;
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status2 != 0 ? status2 : status);
+}
+
+/*
+ * Handle cleanup when the write lock condition variable
+ * wait is cancelled.
+ *
+ * Simply record that the thread is no longer waiting,
+ * and unlock the mutex.
+ */
+static void rwl_writecleanup (void *arg)
+{
+    vg_rwlock_t *rwl = (vg_rwlock_t *)arg;
+
+    rwl->w_wait--;
+    pthread_mutex_unlock (&rwl->mutex);
+}
+
+/*
+ * Lock a read-write lock for write access.
+ */
+static int rwl_writelock (vg_rwlock_t *rwl)
+{
+    int status;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active || rwl->r_active > 0) {
+        rwl->w_wait++;
+        pthread_cleanup_push (rwl_writecleanup, (void*)rwl);
+        while (rwl->w_active || rwl->r_active > 0) {
+            status = pthread_cond_wait (&rwl->write, &rwl->mutex);
+            if (status != 0)
+                break;
+        }
+        pthread_cleanup_pop (0);
+        rwl->w_wait--;
+    }
+    if (status == 0)
+        rwl->w_active = 1;
+    pthread_mutex_unlock (&rwl->mutex);
+    return status;
+}
+
+/*
+ * Attempt to lock a read-write lock for write access. Don't
+ * block if unavailable.
+ */
+static int rwl_writetrylock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+    if (rwl->w_active || rwl->r_active > 0)
+        status = EBUSY;
+    else
+        rwl->w_active = 1;
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status != 0 ? status : status2);
+}
+
+/*
+ * Unlock a read-write lock, using the r_active and w_active fields to
+ * decide whether we're in a read or write lock.
+ */
+static int rwl_unlock (vg_rwlock_t *rwl)
+{
+    int status, status2;
+
+    if (rwl->valid != VG_RWLOCK_VALID)
+        return EINVAL;
+    status = pthread_mutex_lock (&rwl->mutex);
+    if (status != 0)
+        return status;
+
+    if (rwl->r_active > 0) {
+
+       /* READ case */
+       assert(!rwl->w_active);
+       rwl->r_active--;
+       if (rwl->r_active == 0 && rwl->w_wait > 0)
+          status = pthread_cond_signal (&rwl->write);
+       /* END READ case */
+
+    } else {
+
+       /* WRITE case */
+       assert(rwl->w_active);
+       assert(rwl->r_active == 0);
+       rwl->w_active = 0;
+
+       if (rwl->pref_writer) {
+          /* Do writer-preference wakeups. */
+          if (rwl->w_wait > 0) {
+             status = pthread_cond_signal (&rwl->write);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          } else if (rwl->r_wait > 0) {
+             status = pthread_cond_broadcast (&rwl->read);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+         }
+       } else {
+          /* Do reader-preference wakeups. */
+          if (rwl->r_wait > 0) {
+             status = pthread_cond_broadcast (&rwl->read);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          } else if (rwl->w_wait > 0) {
+             status = pthread_cond_signal (&rwl->write);
+             if (status != 0) {
+                pthread_mutex_unlock (&rwl->mutex);
+                return status;
+             }
+          }
+       }
+       /* END WRITE case */
+
+    }
+
+    status2 = pthread_mutex_unlock (&rwl->mutex);
+    return (status2 == 0 ? status : status2);
+}
+
+/* -------------------------------- */
+
+int pthread_rwlock_init ( pthread_rwlock_t* orig,
+                          const pthread_rwlockattr_t* attr )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_init\n");
+   /* Force the remapper to initialise the shadow. */
+   orig->__rw_readers = 0;
+   /* Install the lock preference; the remapper needs to know it. */
+   orig->__rw_kind = PTHREAD_RWLOCK_DEFAULT_NP;
+   if (attr)
+      orig->__rw_kind = attr->__lockkind;
+   rwl = rw_remap ( orig );
+   res = rwl_init ( rwl );
+   return res;
+}
+
+int pthread_rwlock_destroy ( pthread_rwlock_t *orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_destroy\n");
+   rwl = rw_remap ( orig );
+   res = rwl_destroy ( rwl );
+   return res;
+}
+
+int pthread_rwlock_rdlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_rdlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_readlock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_tryrdlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_tryrdlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_readtrylock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_wrlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_wrlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_writelock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_trywrlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_writetrylock ( rwl );
+   return res;
+}
+
+int pthread_rwlock_unlock ( pthread_rwlock_t* orig )
+{
+   int res;
+   vg_rwlock_t* rwl;
+   if (0) printf ("pthread_rwlock_unlock\n");
+   rwl = rw_remap ( orig );
+   res = rwl_unlock ( rwl );
+   return res;
+}
+
+
 /* ---------------------------------------------------------------------
    B'stard.
    ------------------------------------------------------------------ */
@@ -1779,41 +2274,15 @@ weak_alias(__fork, fork)
 
 /*--------------------------------------------------*/
 
-int
-pthread_rwlock_rdlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_rdlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_rdlock, __pthread_rwlock_rdlock)
-
-
-int
-pthread_rwlock_unlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_unlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_unlock, __pthread_rwlock_unlock)
-
-
-int
-pthread_rwlock_wrlock (void* /* pthread_rwlock_t* */ rwlock)
-{
-   static int moans = N_MOANS;
-   if (moans-- > 0) 
-      kludged("pthread_rwlock_wrlock");
-   return 0;
-}
-
 weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock)
 
+weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
+weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
+weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
+weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
+
 
 /* I've no idea what these are, but they get called quite a lot.
    Anybody know? */
index 16b251ecbb0affcc1cf8a426651fc298ac6f447f..de92468019ecc14d72349f82949552c10b5f4389 100644 (file)
@@ -130,13 +130,13 @@ void pthread_mutex_timedlock ( void )  { unimp("pthread_mutex_timedlock"); }
 //void pthread_mutexattr_destroy ( void )  { unimp("pthread_mutexattr_destroy"); }
 //void pthread_mutexattr_init ( void )  { unimp("pthread_mutexattr_init"); }
 //void pthread_once ( void )  { unimp("pthread_once"); }
-void pthread_rwlock_destroy ( void )  { unimp("pthread_rwlock_destroy"); }
-void pthread_rwlock_init ( void )  { unimp("pthread_rwlock_init"); }
+//void pthread_rwlock_destroy ( void )  { unimp("pthread_rwlock_destroy"); }
+//void pthread_rwlock_init ( void )  { unimp("pthread_rwlock_init"); }
 //void pthread_rwlock_rdlock ( void )  { unimp("pthread_rwlock_rdlock"); }
 void pthread_rwlock_timedrdlock ( void )  { unimp("pthread_rwlock_timedrdlock"); }
 void pthread_rwlock_timedwrlock ( void )  { unimp("pthread_rwlock_timedwrlock"); }
-void pthread_rwlock_tryrdlock ( void )  { unimp("pthread_rwlock_tryrdlock"); }
-void pthread_rwlock_trywrlock ( void )  { unimp("pthread_rwlock_trywrlock"); }
+//void pthread_rwlock_tryrdlock ( void )  { unimp("pthread_rwlock_tryrdlock"); }
+//void pthread_rwlock_trywrlock ( void )  { unimp("pthread_rwlock_trywrlock"); }
 //void pthread_rwlock_unlock ( void )  { unimp("pthread_rwlock_unlock"); }
 //void pthread_rwlock_wrlock ( void )  { unimp("pthread_rwlock_wrlock"); }
 void pthread_rwlockattr_destroy ( void )  { unimp("pthread_rwlockattr_destroy"); }
@@ -193,10 +193,10 @@ void pthread_attr_init@GLIBC_2.0 ( void )  { unimp("pthread_attr_init@GLIBC_2.0"
 # define weak_alias(name, aliasname) \
   extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
 
-weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
-weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
-weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
-weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
+//weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy)
+//weak_alias(pthread_rwlock_init, __pthread_rwlock_init)
+//weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
+//weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
 //weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock)
 weak_alias(_IO_ftrylockfile, ftrylockfile)