From a257625c81ab5f535ddebeb98ebee3d8eeab0ec8 Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Mon, 1 Oct 2012 23:42:35 +0000
Subject: Prevent deadlock during close.

---
 .../com/amazon/carbonado/txn/TransactionScope.java | 42 +++++++++++++---------
 1 file changed, 25 insertions(+), 17 deletions(-)

(limited to 'src/main')

diff --git a/src/main/java/com/amazon/carbonado/txn/TransactionScope.java b/src/main/java/com/amazon/carbonado/txn/TransactionScope.java
index 07cb826..b39d091 100644
--- a/src/main/java/com/amazon/carbonado/txn/TransactionScope.java
+++ b/src/main/java/com/amazon/carbonado/txn/TransactionScope.java
@@ -319,31 +319,39 @@ public class TransactionScope<Txn> {
      */
     void close() throws RepositoryException {
         mLock.lock();
+        if (mClosed) {
+            mLock.unlock();
+            return;
+        }
+ 
+        Map<Class<?>, CursorList<TransactionImpl<Txn>>> cursors;
         try {
-            if (!mClosed) {
-                while (mActive != null) {
-                    mActive.exit();
-                }
-                if (mCursors != null) {
-                    try {
-                        for (CursorList<TransactionImpl<Txn>> cursorList : mCursors.values()) {
-                            cursorList.closeCursors();
-                        }
-                    } finally {
-                        // Ensure that map is freed promptly. Thread-local
-                        // reference to this scope otherwise keeps map and its
-                        // contents lingering around for a very long time.
-                        mCursors = null;
-                    }
-                }
+            cursors = mCursors;        
+
+            // Ensure that map is freed promptly. Thread-local reference to
+            // this scope otherwise keeps map and its contents lingering around
+            // for a very long time.
+            mCursors = null;
+
+            while (mActive != null) {
+                mActive.exit();
             }
         } finally {
-            mClosed = true;
             // Swap TransactionManager out with a dummy one, to allow the
             // original one to get freed.
             mTxnMgr = (TransactionManager<Txn>) TransactionManager.Closed.THE;
+            mClosed = true;
             mLock.unlock();
         }
+
+        // Close cursors without lock held, to prevent deadlock. Cursor close
+        // operation might acquire its own lock, which in turn acquires a lock
+        // on this scope.
+        if (cursors != null) {
+            for (CursorList<TransactionImpl<Txn>> cursorList : cursors.values()) {
+                cursorList.closeCursors();
+            }
+        }
     }
 
     /**
-- 
cgit v1.2.3