THRIFT-1272 add subclass of ReadWriteMutex that avoids writer
starvation
Patch: Dave Watson

git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1213067 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/concurrency/Mutex.cpp b/lib/cpp/src/concurrency/Mutex.cpp
index 332d415..4f3f323 100644
--- a/lib/cpp/src/concurrency/Mutex.cpp
+++ b/lib/cpp/src/concurrency/Mutex.cpp
@@ -270,9 +270,9 @@
     PROFILE_MUTEX_LOCKED();
   }
 
-  bool attemptRead() const { return pthread_rwlock_tryrdlock(&rw_lock_); }
+  bool attemptRead() const { return !pthread_rwlock_tryrdlock(&rw_lock_); }
 
-  bool attemptWrite() const { return pthread_rwlock_trywrlock(&rw_lock_); }
+  bool attemptWrite() const { return !pthread_rwlock_trywrlock(&rw_lock_); }
 
   void release() const {
     PROFILE_MUTEX_START_UNLOCK();
@@ -300,5 +300,35 @@
 
 void ReadWriteMutex::release() const { impl_->release(); }
 
+NoStarveReadWriteMutex::NoStarveReadWriteMutex() : writerWaiting_(false) {}
+
+void NoStarveReadWriteMutex::acquireRead() const
+{
+  if (writerWaiting_) {
+    // writer is waiting, block on the writer's mutex until he's done with it
+    mutex_.lock();
+    mutex_.unlock();
+  }
+
+  ReadWriteMutex::acquireRead();
+}
+
+void NoStarveReadWriteMutex::acquireWrite() const
+{
+  // if we can acquire the rwlock the easy way, we're done
+  if (attemptWrite()) {
+    return;
+  }
+
+  // failed to get the rwlock, do it the hard way:
+  // locking the mutex and setting writerWaiting will cause all new readers to
+  // block on the mutex rather than on the rwlock.
+  mutex_.lock();
+  writerWaiting_ = true;
+  ReadWriteMutex::acquireWrite();
+  writerWaiting_ = false;
+  mutex_.unlock();
+}
+
 }}} // apache::thrift::concurrency
 
diff --git a/lib/cpp/src/concurrency/Mutex.h b/lib/cpp/src/concurrency/Mutex.h
index 847aaf6..1763b5c 100644
--- a/lib/cpp/src/concurrency/Mutex.h
+++ b/lib/cpp/src/concurrency/Mutex.h
@@ -101,6 +101,25 @@
   boost::shared_ptr<impl> impl_;
 };
 
+/**
+ * A ReadWriteMutex that guarantees writers will not be starved by readers:
+ * When a writer attempts to acquire the mutex, all new readers will be
+ * blocked from acquiring the mutex until the writer has acquired and
+ * released it. In some operating systems, this may already be guaranteed
+ * by a regular ReadWriteMutex.
+ */
+class NoStarveReadWriteMutex : public ReadWriteMutex {
+public:
+  NoStarveReadWriteMutex();
+
+  virtual void acquireRead() const;
+  virtual void acquireWrite() const;
+
+private:
+  Mutex mutex_;
+  mutable volatile bool writerWaiting_;
+};
+
 class Guard : boost::noncopyable {
  public:
   Guard(const Mutex& value, int64_t timeout = 0) : mutex_(&value) {