Remove old rwlock implementation and replace with home-grown one.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@346 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c
index 6f51b04..953f3d1 100644
--- a/coregrind/arch/x86-linux/vg_libpthread.c
+++ b/coregrind/arch/x86-linux/vg_libpthread.c
@@ -2034,64 +2034,23 @@
 
 
 /* ---------------------------------------------------------------------
-   Hacky implementation of reader-writer locks.
+   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.
- */
-
-/*
- * 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}
+typedef 
+   struct {
+      int             initted;  /* != 0 --> in use; sanity check only */
+      int             prefer_w; /* != 0 --> prefer writer */
+      int             nwait_r;  /* # of waiting readers */
+      int             nwait_w;  /* # of waiting writers */
+      pthread_cond_t  cv_r;     /* for signalling readers */
+      pthread_cond_t  cv_w;     /* for signalling writers */
+      pthread_mutex_t mx;
+      int             status;
+      /* allowed range for status: >= -1.  -1 means 1 writer currently
+         active, >= 0 means N readers currently active. */
+   } 
+   vg_rwlock_t;
 
 
 static pthread_mutex_t rw_remap_mx = PTHREAD_MUTEX_INITIALIZER;
@@ -2100,6 +2059,23 @@
 static pthread_rwlock_t*   rw_remap_orig[VG_N_RWLOCKS];
 static vg_rwlock_t         rw_remap_new[VG_N_RWLOCKS];
 
+
+static 
+void init_vg_rwlock ( vg_rwlock_t* vg_rwl )
+{
+   int res = 0;
+   vg_rwl->initted = 1;
+   vg_rwl->prefer_w = 1;
+   vg_rwl->nwait_r = 0;
+   vg_rwl->nwait_w = 0;
+   vg_rwl->status = 0;
+   res = pthread_mutex_init(&vg_rwl->mx, NULL);
+   res |= pthread_cond_init(&vg_rwl->cv_r, NULL);
+   res |= pthread_cond_init(&vg_rwl->cv_w, NULL);
+   assert(res == 0);
+}
+
+
 /* 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
@@ -2120,333 +2096,34 @@
    }
    if (i == rw_remap_used) {
       if (rw_remap_used == VG_N_RWLOCKS) {
-         res = pthread_mutex_unlock(&rw_remap_mx);
+         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;
+      rw_remap_new[i].initted = 0;
       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. */
+   /* Initialise the shadow, if required. */
    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;
+      init_vg_rwlock(vg_rwl);
       if (orig->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
-         vg_rwl->pref_writer = 0;
+         vg_rwl->prefer_w = 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. */
@@ -2456,68 +2133,247 @@
    if (attr)
       orig->__rw_kind = attr->__lockkind;
    rwl = rw_remap ( orig );
-   res = rwl_init ( rwl );
-   return res;
+   return 0;
 }
 
-int pthread_rwlock_destroy ( pthread_rwlock_t *orig )
+
+static 
+void pthread_rwlock_rdlock_CANCEL_HDLR ( void* rwl_v )
 {
-   int res;
-   vg_rwlock_t* rwl;
-   if (0) printf ("pthread_rwlock_destroy\n");
-   rwl = rw_remap ( orig );
-   res = rwl_destroy ( rwl );
-   return res;
+   vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v;
+   rwl->nwait_r--;
+   pthread_mutex_unlock (&rwl->mx);
 }
 
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status < 0) {
+      assert(rwl->status == -1);
+      rwl->nwait_r++;
+      pthread_cleanup_push( pthread_rwlock_rdlock_CANCEL_HDLR, rwl );
+      while (1) {
+         if (rwl->status == 0) break;
+         res = pthread_cond_wait(&rwl->cv_r, &rwl->mx);
+         assert(res == 0);
+      }
+      pthread_cleanup_pop(0);
+      rwl->nwait_r--;
+   }
+   assert(rwl->status >= 0);
+   rwl->status++;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status == -1) {
+      /* Writer active; we have to give up. */
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   /* Success */
+   assert(rwl->status >= 0);
+   rwl->status++;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
+static 
+void pthread_rwlock_wrlock_CANCEL_HDLR ( void* rwl_v )
+{
+   vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v;
+   rwl->nwait_w--;
+   pthread_mutex_unlock (&rwl->mx);
+}
+
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0) {
+      rwl->nwait_w++;
+      pthread_cleanup_push( pthread_rwlock_wrlock_CANCEL_HDLR, rwl );
+      while (1) {
+         if (rwl->status == 0) break;
+         res = pthread_cond_wait(&rwl->cv_w, &rwl->mx);
+         assert(res == 0);
+      }
+      pthread_cleanup_pop(0);
+      rwl->nwait_w--;
+   }
+   assert(rwl->status == 0);
+   rwl->status = -1;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig )
 {
    int res;
    vg_rwlock_t* rwl;
-   if (0) printf ("pthread_rwlock_trywrlock\n");
+   if (0) printf ("pthread_wrlock_trywrlock\n");
    rwl = rw_remap ( orig );
-   res = rwl_writetrylock ( rwl );
-   return res;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0) {
+      /* Reader(s) or a writer active; we have to give up. */
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   /* Success */
+   assert(rwl->status == 0);
+   rwl->status = -1;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 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;
+   rwl = rw_remap ( orig );
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status == 0) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EPERM;
+   }
+   assert(rwl->status != 0);
+   if (rwl->status == -1) {
+     rwl->status = 0;
+   } else {
+     assert(rwl->status > 0);
+     rwl->status--;
+   }
+
+   assert(rwl->status >= 0);
+
+   if (rwl->prefer_w) {
+
+      /* Favour waiting writers, if any. */
+      if (rwl->nwait_w > 0) {
+         /* Writer(s) are waiting. */
+         if (rwl->status == 0) {
+            /* We can let a writer in. */
+            res = pthread_cond_signal(&rwl->cv_w);
+            assert(res == 0);
+         } else {
+            /* There are still readers active.  Do nothing; eventually
+               they will disappear, at which point a writer will be
+               admitted. */
+         }
+      } 
+      else
+      /* No waiting writers. */
+      if (rwl->nwait_r > 0) {
+         /* Let in a waiting reader. */
+         res = pthread_cond_signal(&rwl->cv_r);
+         assert(res == 0);
+      }
+
+   } else {
+
+      /* Favour waiting readers, if any. */
+      if (rwl->nwait_r > 0) {
+         /* Reader(s) are waiting; let one in. */
+         res = pthread_cond_signal(&rwl->cv_r);
+         assert(res == 0);
+      } 
+      else
+      /* No waiting readers. */
+      if (rwl->nwait_w > 0 && rwl->status == 0) {
+         /* We have waiting writers and no active readers; let a
+            writer in. */
+         res = pthread_cond_signal(&rwl->cv_w);
+         assert(res == 0);
+      }
+   }
+
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;   
+}
+
+
+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 = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0 || rwl->nwait_r > 0 || rwl->nwait_w > 0) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   rwl->initted = 0;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
 
diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c
index 6f51b04..953f3d1 100644
--- a/coregrind/vg_libpthread.c
+++ b/coregrind/vg_libpthread.c
@@ -2034,64 +2034,23 @@
 
 
 /* ---------------------------------------------------------------------
-   Hacky implementation of reader-writer locks.
+   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.
- */
-
-/*
- * 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}
+typedef 
+   struct {
+      int             initted;  /* != 0 --> in use; sanity check only */
+      int             prefer_w; /* != 0 --> prefer writer */
+      int             nwait_r;  /* # of waiting readers */
+      int             nwait_w;  /* # of waiting writers */
+      pthread_cond_t  cv_r;     /* for signalling readers */
+      pthread_cond_t  cv_w;     /* for signalling writers */
+      pthread_mutex_t mx;
+      int             status;
+      /* allowed range for status: >= -1.  -1 means 1 writer currently
+         active, >= 0 means N readers currently active. */
+   } 
+   vg_rwlock_t;
 
 
 static pthread_mutex_t rw_remap_mx = PTHREAD_MUTEX_INITIALIZER;
@@ -2100,6 +2059,23 @@
 static pthread_rwlock_t*   rw_remap_orig[VG_N_RWLOCKS];
 static vg_rwlock_t         rw_remap_new[VG_N_RWLOCKS];
 
+
+static 
+void init_vg_rwlock ( vg_rwlock_t* vg_rwl )
+{
+   int res = 0;
+   vg_rwl->initted = 1;
+   vg_rwl->prefer_w = 1;
+   vg_rwl->nwait_r = 0;
+   vg_rwl->nwait_w = 0;
+   vg_rwl->status = 0;
+   res = pthread_mutex_init(&vg_rwl->mx, NULL);
+   res |= pthread_cond_init(&vg_rwl->cv_r, NULL);
+   res |= pthread_cond_init(&vg_rwl->cv_w, NULL);
+   assert(res == 0);
+}
+
+
 /* 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
@@ -2120,333 +2096,34 @@
    }
    if (i == rw_remap_used) {
       if (rw_remap_used == VG_N_RWLOCKS) {
-         res = pthread_mutex_unlock(&rw_remap_mx);
+         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;
+      rw_remap_new[i].initted = 0;
       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. */
+   /* Initialise the shadow, if required. */
    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;
+      init_vg_rwlock(vg_rwl);
       if (orig->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
-         vg_rwl->pref_writer = 0;
+         vg_rwl->prefer_w = 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. */
@@ -2456,68 +2133,247 @@
    if (attr)
       orig->__rw_kind = attr->__lockkind;
    rwl = rw_remap ( orig );
-   res = rwl_init ( rwl );
-   return res;
+   return 0;
 }
 
-int pthread_rwlock_destroy ( pthread_rwlock_t *orig )
+
+static 
+void pthread_rwlock_rdlock_CANCEL_HDLR ( void* rwl_v )
 {
-   int res;
-   vg_rwlock_t* rwl;
-   if (0) printf ("pthread_rwlock_destroy\n");
-   rwl = rw_remap ( orig );
-   res = rwl_destroy ( rwl );
-   return res;
+   vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v;
+   rwl->nwait_r--;
+   pthread_mutex_unlock (&rwl->mx);
 }
 
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status < 0) {
+      assert(rwl->status == -1);
+      rwl->nwait_r++;
+      pthread_cleanup_push( pthread_rwlock_rdlock_CANCEL_HDLR, rwl );
+      while (1) {
+         if (rwl->status == 0) break;
+         res = pthread_cond_wait(&rwl->cv_r, &rwl->mx);
+         assert(res == 0);
+      }
+      pthread_cleanup_pop(0);
+      rwl->nwait_r--;
+   }
+   assert(rwl->status >= 0);
+   rwl->status++;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status == -1) {
+      /* Writer active; we have to give up. */
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   /* Success */
+   assert(rwl->status >= 0);
+   rwl->status++;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
+static 
+void pthread_rwlock_wrlock_CANCEL_HDLR ( void* rwl_v )
+{
+   vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v;
+   rwl->nwait_w--;
+   pthread_mutex_unlock (&rwl->mx);
+}
+
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0) {
+      rwl->nwait_w++;
+      pthread_cleanup_push( pthread_rwlock_wrlock_CANCEL_HDLR, rwl );
+      while (1) {
+         if (rwl->status == 0) break;
+         res = pthread_cond_wait(&rwl->cv_w, &rwl->mx);
+         assert(res == 0);
+      }
+      pthread_cleanup_pop(0);
+      rwl->nwait_w--;
+   }
+   assert(rwl->status == 0);
+   rwl->status = -1;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig )
 {
    int res;
    vg_rwlock_t* rwl;
-   if (0) printf ("pthread_rwlock_trywrlock\n");
+   if (0) printf ("pthread_wrlock_trywrlock\n");
    rwl = rw_remap ( orig );
-   res = rwl_writetrylock ( rwl );
-   return res;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0) {
+      /* Reader(s) or a writer active; we have to give up. */
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   /* Success */
+   assert(rwl->status == 0);
+   rwl->status = -1;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 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;
+   rwl = rw_remap ( orig );
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status == 0) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EPERM;
+   }
+   assert(rwl->status != 0);
+   if (rwl->status == -1) {
+     rwl->status = 0;
+   } else {
+     assert(rwl->status > 0);
+     rwl->status--;
+   }
+
+   assert(rwl->status >= 0);
+
+   if (rwl->prefer_w) {
+
+      /* Favour waiting writers, if any. */
+      if (rwl->nwait_w > 0) {
+         /* Writer(s) are waiting. */
+         if (rwl->status == 0) {
+            /* We can let a writer in. */
+            res = pthread_cond_signal(&rwl->cv_w);
+            assert(res == 0);
+         } else {
+            /* There are still readers active.  Do nothing; eventually
+               they will disappear, at which point a writer will be
+               admitted. */
+         }
+      } 
+      else
+      /* No waiting writers. */
+      if (rwl->nwait_r > 0) {
+         /* Let in a waiting reader. */
+         res = pthread_cond_signal(&rwl->cv_r);
+         assert(res == 0);
+      }
+
+   } else {
+
+      /* Favour waiting readers, if any. */
+      if (rwl->nwait_r > 0) {
+         /* Reader(s) are waiting; let one in. */
+         res = pthread_cond_signal(&rwl->cv_r);
+         assert(res == 0);
+      } 
+      else
+      /* No waiting readers. */
+      if (rwl->nwait_w > 0 && rwl->status == 0) {
+         /* We have waiting writers and no active readers; let a
+            writer in. */
+         res = pthread_cond_signal(&rwl->cv_w);
+         assert(res == 0);
+      }
+   }
+
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;   
+}
+
+
+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 = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0 || rwl->nwait_r > 0 || rwl->nwait_w > 0) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   rwl->initted = 0;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
 
diff --git a/vg_libpthread.c b/vg_libpthread.c
index 6f51b04..953f3d1 100644
--- a/vg_libpthread.c
+++ b/vg_libpthread.c
@@ -2034,64 +2034,23 @@
 
 
 /* ---------------------------------------------------------------------
-   Hacky implementation of reader-writer locks.
+   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.
- */
-
-/*
- * 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}
+typedef 
+   struct {
+      int             initted;  /* != 0 --> in use; sanity check only */
+      int             prefer_w; /* != 0 --> prefer writer */
+      int             nwait_r;  /* # of waiting readers */
+      int             nwait_w;  /* # of waiting writers */
+      pthread_cond_t  cv_r;     /* for signalling readers */
+      pthread_cond_t  cv_w;     /* for signalling writers */
+      pthread_mutex_t mx;
+      int             status;
+      /* allowed range for status: >= -1.  -1 means 1 writer currently
+         active, >= 0 means N readers currently active. */
+   } 
+   vg_rwlock_t;
 
 
 static pthread_mutex_t rw_remap_mx = PTHREAD_MUTEX_INITIALIZER;
@@ -2100,6 +2059,23 @@
 static pthread_rwlock_t*   rw_remap_orig[VG_N_RWLOCKS];
 static vg_rwlock_t         rw_remap_new[VG_N_RWLOCKS];
 
+
+static 
+void init_vg_rwlock ( vg_rwlock_t* vg_rwl )
+{
+   int res = 0;
+   vg_rwl->initted = 1;
+   vg_rwl->prefer_w = 1;
+   vg_rwl->nwait_r = 0;
+   vg_rwl->nwait_w = 0;
+   vg_rwl->status = 0;
+   res = pthread_mutex_init(&vg_rwl->mx, NULL);
+   res |= pthread_cond_init(&vg_rwl->cv_r, NULL);
+   res |= pthread_cond_init(&vg_rwl->cv_w, NULL);
+   assert(res == 0);
+}
+
+
 /* 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
@@ -2120,333 +2096,34 @@
    }
    if (i == rw_remap_used) {
       if (rw_remap_used == VG_N_RWLOCKS) {
-         res = pthread_mutex_unlock(&rw_remap_mx);
+         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;
+      rw_remap_new[i].initted = 0;
       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. */
+   /* Initialise the shadow, if required. */
    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;
+      init_vg_rwlock(vg_rwl);
       if (orig->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
-         vg_rwl->pref_writer = 0;
+         vg_rwl->prefer_w = 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. */
@@ -2456,68 +2133,247 @@
    if (attr)
       orig->__rw_kind = attr->__lockkind;
    rwl = rw_remap ( orig );
-   res = rwl_init ( rwl );
-   return res;
+   return 0;
 }
 
-int pthread_rwlock_destroy ( pthread_rwlock_t *orig )
+
+static 
+void pthread_rwlock_rdlock_CANCEL_HDLR ( void* rwl_v )
 {
-   int res;
-   vg_rwlock_t* rwl;
-   if (0) printf ("pthread_rwlock_destroy\n");
-   rwl = rw_remap ( orig );
-   res = rwl_destroy ( rwl );
-   return res;
+   vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v;
+   rwl->nwait_r--;
+   pthread_mutex_unlock (&rwl->mx);
 }
 
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status < 0) {
+      assert(rwl->status == -1);
+      rwl->nwait_r++;
+      pthread_cleanup_push( pthread_rwlock_rdlock_CANCEL_HDLR, rwl );
+      while (1) {
+         if (rwl->status == 0) break;
+         res = pthread_cond_wait(&rwl->cv_r, &rwl->mx);
+         assert(res == 0);
+      }
+      pthread_cleanup_pop(0);
+      rwl->nwait_r--;
+   }
+   assert(rwl->status >= 0);
+   rwl->status++;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status == -1) {
+      /* Writer active; we have to give up. */
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   /* Success */
+   assert(rwl->status >= 0);
+   rwl->status++;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
+static 
+void pthread_rwlock_wrlock_CANCEL_HDLR ( void* rwl_v )
+{
+   vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v;
+   rwl->nwait_w--;
+   pthread_mutex_unlock (&rwl->mx);
+}
+
+
 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;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0) {
+      rwl->nwait_w++;
+      pthread_cleanup_push( pthread_rwlock_wrlock_CANCEL_HDLR, rwl );
+      while (1) {
+         if (rwl->status == 0) break;
+         res = pthread_cond_wait(&rwl->cv_w, &rwl->mx);
+         assert(res == 0);
+      }
+      pthread_cleanup_pop(0);
+      rwl->nwait_w--;
+   }
+   assert(rwl->status == 0);
+   rwl->status = -1;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig )
 {
    int res;
    vg_rwlock_t* rwl;
-   if (0) printf ("pthread_rwlock_trywrlock\n");
+   if (0) printf ("pthread_wrlock_trywrlock\n");
    rwl = rw_remap ( orig );
-   res = rwl_writetrylock ( rwl );
-   return res;
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0) {
+      /* Reader(s) or a writer active; we have to give up. */
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   /* Success */
+   assert(rwl->status == 0);
+   rwl->status = -1;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }
 
+
 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;
+   rwl = rw_remap ( orig );
+   res = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status == 0) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EPERM;
+   }
+   assert(rwl->status != 0);
+   if (rwl->status == -1) {
+     rwl->status = 0;
+   } else {
+     assert(rwl->status > 0);
+     rwl->status--;
+   }
+
+   assert(rwl->status >= 0);
+
+   if (rwl->prefer_w) {
+
+      /* Favour waiting writers, if any. */
+      if (rwl->nwait_w > 0) {
+         /* Writer(s) are waiting. */
+         if (rwl->status == 0) {
+            /* We can let a writer in. */
+            res = pthread_cond_signal(&rwl->cv_w);
+            assert(res == 0);
+         } else {
+            /* There are still readers active.  Do nothing; eventually
+               they will disappear, at which point a writer will be
+               admitted. */
+         }
+      } 
+      else
+      /* No waiting writers. */
+      if (rwl->nwait_r > 0) {
+         /* Let in a waiting reader. */
+         res = pthread_cond_signal(&rwl->cv_r);
+         assert(res == 0);
+      }
+
+   } else {
+
+      /* Favour waiting readers, if any. */
+      if (rwl->nwait_r > 0) {
+         /* Reader(s) are waiting; let one in. */
+         res = pthread_cond_signal(&rwl->cv_r);
+         assert(res == 0);
+      } 
+      else
+      /* No waiting readers. */
+      if (rwl->nwait_w > 0 && rwl->status == 0) {
+         /* We have waiting writers and no active readers; let a
+            writer in. */
+         res = pthread_cond_signal(&rwl->cv_w);
+         assert(res == 0);
+      }
+   }
+
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;   
+}
+
+
+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 = __pthread_mutex_lock(&rwl->mx);
+   assert(res == 0);
+   if (!rwl->initted) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EINVAL;
+   }
+   if (rwl->status != 0 || rwl->nwait_r > 0 || rwl->nwait_w > 0) {
+      res = __pthread_mutex_unlock(&rwl->mx);
+      assert(res == 0);
+      return EBUSY;
+   }
+   rwl->initted = 0;
+   res = __pthread_mutex_unlock(&rwl->mx);
+   assert(res == 0);
+   return 0;
 }