From 31f88f98c97f5645c045aaca7de76dafe3e63aec Mon Sep 17 00:00:00 2001
From: Philippe Canal <pcanal@fnal.gov>
Date: Fri, 20 Oct 2017 13:47:47 -0500
Subject: [PATCH] RWLock: handle case of writer meeting a nested reader.

Prior to this update if a thread requested the write lock when another thread had two or more nested
read lock take, the code would dead-lock
---
 core/thread/inc/ROOT/TReentrantRWLock.hxx |  8 ++++++++
 core/thread/src/TReentrantRWLock.cxx      | 17 ++++++++++++++++-
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/core/thread/inc/ROOT/TReentrantRWLock.hxx b/core/thread/inc/ROOT/TReentrantRWLock.hxx
index 6f6a3879a6c..a616b8d9669 100644
--- a/core/thread/inc/ROOT/TReentrantRWLock.hxx
+++ b/core/thread/inc/ROOT/TReentrantRWLock.hxx
@@ -50,6 +50,10 @@ struct UniqueLockRecurseCount {
    template <typename MutexT>
    void DecrementReadCount(local_t &local, MutexT &) { DecrementReadCount(local); }
 
+   void ResetReadCount(local_t &local, int newvalue) {
+      local->fReadersCount = newvalue;
+   }
+
    bool IsNotCurrentWriter(local_t &local) { return !local->fIsWriter; }
 
    void SetIsWriter(local_t &local)
@@ -97,6 +101,10 @@ struct RecurseCounts {
       DecrementReadCount(local);
    }
 
+   void ResetReadCount(local_t &local, int newvalue) {
+      fReadersCount[local] = newvalue;
+   }
+
    bool IsNotCurrentWriter(local_t &local) { return fWriterThread != local; }
 
    void SetIsWriter(local_t &local)
diff --git a/core/thread/src/TReentrantRWLock.cxx b/core/thread/src/TReentrantRWLock.cxx
index f04ab29c84b..457fd87b130 100644
--- a/core/thread/src/TReentrantRWLock.cxx
+++ b/core/thread/src/TReentrantRWLock.cxx
@@ -35,6 +35,7 @@ thus preventing starvation.
 #include "ROOT/TSpinMutex.hxx"
 #include "TMutex.h"
 #include "TError.h"
+#include <assert.h>
 
 using namespace ROOT;
 
@@ -75,7 +76,21 @@ void TReentrantRWLock<MutexT, RecurseCountsT>::ReadLock()
       std::unique_lock<MutexT> lock(fMutex);
 
       // Wait for writers, if any
-      if (fWriter && fRecurseCounts.IsNotCurrentWriter(local)) fCond.wait(lock, [this] { return !fWriter; });
+      if (fWriter && fRecurseCounts.IsNotCurrentWriter(local)) {
+         auto readerCount = fRecurseCounts.GetLocalReadersCount(local);
+         if (readerCount == 0)
+            fCond.wait(lock, [this] { return !fWriter; });
+         // else
+         //   There is a writer **but** we have outstanding readers
+         //   locks, this must mean that the writer is actually
+         //   waiting on this thread to release its read locks.
+         //   This can be done in only two ways:
+         //     * request the writer lock
+         //     * release the reader lock
+         //   Either way, this thread needs to proceed to
+         //   be able to reach a point whether it does one
+         //   of the two.
+      }
 
       fRecurseCounts.IncrementReadCount(local);
 
-- 
GitLab