You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ck...@apache.org on 2009/01/14 15:04:51 UTC

svn commit: r734400 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/lock/ main/java/org/apache/jackrabbit/core/state/ test/java/org/apache/jackrabbit/core/ test/java/org/apache/jack...

Author: ckoell
Date: Wed Jan 14 06:04:17 2009
New Revision: 734400

URL: http://svn.apache.org/viewvc?rev=734400&view=rev
Log:
JCR-1334 Deadlock due different Thread access in same Transaction

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TimeBomb.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java Wed Jan 14 06:04:17 2009
@@ -21,6 +21,9 @@
 import org.apache.jackrabbit.util.Timer;
 
 import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -47,6 +50,11 @@
     private static final int STATUS_ROLLED_BACK = 6;
 
     /**
+     * The per thread associated Xid
+     */
+    private static ThreadLocal CURRENT_XID = new ThreadLocal();
+
+    /**
      * Create a global timer for all transaction contexts.
      */
     private static final Timer TIMER = new Timer(true);
@@ -62,6 +70,11 @@
     private final int timeout;
 
     /**
+    * The Xid
+    */
+   private final Xid xid;
+   
+    /**
      * Transaction attributes.
      */
     private final Map attributes = new HashMap();
@@ -78,10 +91,12 @@
 
     /**
      * Create a new instance of this class.
+     * @param xid associated xid
      * @param resources transactional resources
      * @param timeout timeout, in seconds
      */
-    public TransactionContext(InternalXAResource[] resources, int timeout) {
+    public TransactionContext(Xid xid, InternalXAResource[] resources, int timeout) {
+        this.xid = xid;
         this.resources = resources;
         this.timeout = timeout;
     }
@@ -129,6 +144,7 @@
      * @throws XAException if an error occurs
      */
     public synchronized void prepare() throws XAException {
+        bindCurrentXid();
         status = STATUS_PREPARING;
         beforeOperation();
 
@@ -172,6 +188,7 @@
         if (status == STATUS_ROLLED_BACK) {
             throw new XAException(XAException.XA_RBTIMEOUT);
         }
+        bindCurrentXid();
         status = STATUS_COMMITTING;
         beforeOperation();
 
@@ -197,6 +214,7 @@
 
         // cancel the rollback task
         cancel();
+        cleanCurrentXid();
 
         if (txe != null) {
             XAException e = new XAException(XAException.XA_RBOTHER);
@@ -214,6 +232,7 @@
         if (status == STATUS_ROLLED_BACK) {
             throw new XAException(XAException.XA_RBTIMEOUT);
         }
+        bindCurrentXid();
         status = STATUS_ROLLING_BACK;
         beforeOperation();
 
@@ -232,6 +251,7 @@
 
         // cancel the rollback task
         cancel();
+        cleanCurrentXid();
 
         if (errors != 0) {
             throw new XAException(XAException.XA_RBOTHER);
@@ -295,4 +315,43 @@
     public void setSuspended(boolean suspended) {
         this.suspended = suspended;
     }
+    
+    /**
+     * Helper Method to bind the {@link Xid} associated with this {@link TransactionContext}
+     * to the {@link #CURRENT_XID} ThreadLocal
+     * @param methodName
+     */
+    private void bindCurrentXid() {
+        CURRENT_XID.set(xid);
+    }
+
+    /**
+     * Helper Method to clean the {@link Xid} associated with this {@link TransactionContext}
+     * from the {@link #CURRENT_XID} ThreadLocal
+     * @param methodName
+     */
+    private void cleanCurrentXid() {
+        CURRENT_XID.set(null);
+    }
+    
+    /**
+     * Returns the {@link Xid} bind to the {@link #CURRENT_XID} ThreadLocal
+     * @return current Xid or null
+     */
+    public static Xid getCurrentXid() {
+        return (Xid) CURRENT_XID.get();
+    }
+    
+    /**
+     * Helper Method to check if the given {@link Xid} has the same globalTransactionId
+     * as the current {@link Xid} bind to the {@link #CURRENT_XID} ThreadLocal
+     * @param xid Xid to check
+     * @param fallback if either the given {@link Xid} or the current {@link Xid} is null we can not check if they 
+     *        are same, the fallback value will be returned
+     * @return true if the same otherwise false
+     */
+    public static boolean isCurrentXid(Xid xid, boolean fallback) {
+        Xid currentXid = (Xid) CURRENT_XID.get();
+        return fallback ? true : (currentXid == null || xid == null) ? fallback : Arrays.equals(xid.getGlobalTransactionId(), currentXid.getGlobalTransactionId());  
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java Wed Jan 14 06:04:17 2009
@@ -265,7 +265,7 @@
      * @return transaction context
      */
     private TransactionContext createTransaction(Xid xid) {
-        TransactionContext tx = new TransactionContext(txResources, getTransactionTimeout());
+        TransactionContext tx = new TransactionContext(xid, txResources, getTransactionTimeout());
         txGlobal.put(xid, tx);
         return tx;
     }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java Wed Jan 14 06:04:17 2009
@@ -26,6 +26,7 @@
 import org.apache.jackrabbit.core.PropertyId;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.SessionListener;
+import org.apache.jackrabbit.core.TransactionContext;
 import org.apache.jackrabbit.core.WorkspaceImpl;
 import org.apache.jackrabbit.core.cluster.ClusterOperation;
 import org.apache.jackrabbit.core.cluster.LockEventChannel;
@@ -59,6 +60,8 @@
 import javax.jcr.lock.LockException;
 import javax.jcr.observation.Event;
 import javax.jcr.observation.EventIterator;
+import javax.transaction.xa.Xid;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.IOException;
@@ -89,10 +92,44 @@
      */
     private final PathMap lockMap = new PathMap();
 
-    /**
-     * Lock to path map.
-     */
-    private final ReentrantLock lockMapLock = new ReentrantLock();
+    private final ReentrantLock lockMapLock = new ReentrantLock(){
+        
+        private Xid activeXid;
+        
+        public void acquire() throws InterruptedException {
+            if (Thread.interrupted()) throw new InterruptedException();
+            Thread caller = Thread.currentThread();
+            synchronized(this) {
+                boolean allow = TransactionContext.isCurrentXid(activeXid, caller == owner_);
+                if (allow) {
+                    ++holds_;
+                } else {
+                    try {  
+                        while (owner_ != null) 
+                            wait(); 
+                        owner_ = caller;
+                        activeXid = (Xid) TransactionContext.getCurrentXid();
+                        holds_ = 1;
+                    } catch (InterruptedException ex) {
+                        notify();
+                        throw ex;
+                    }
+                }
+            }
+        }
+        
+        public synchronized void release()  {
+            boolean allow = TransactionContext.isCurrentXid(activeXid, Thread.currentThread() == owner_);
+            if (!allow)
+                throw new Error("Illegal Lock usage"); 
+
+            if (--holds_ == 0) {
+                owner_ = null;
+                activeXid = null;
+                notify(); 
+            }
+        }       
+    };
 
     /**
      * System session

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java Wed Jan 14 06:04:17 2009
@@ -16,7 +16,11 @@
  */
 package org.apache.jackrabbit.core.state;
 
+import javax.transaction.xa.Xid;
+
 import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.TransactionContext;
+
 import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
 import EDU.oswego.cs.dl.util.concurrent.Sync;
 
@@ -48,6 +52,7 @@
         return new WriteLock() {
 
             {
+                rwLock.setActiveXid(TransactionContext.getCurrentXid());
                 rwLock.writeLock().acquire();
             }
 
@@ -55,6 +60,7 @@
              * {@inheritDoc}
              */
             public void release() {
+                rwLock.setActiveXid(null);
                 rwLock.writeLock().release();
             }
 
@@ -88,13 +94,23 @@
 
     private static final class RWLock extends ReentrantWriterPreferenceReadWriteLock {
 
+        private Xid activeXid;
+
         /**
          * Allow reader when there is no active writer, or current thread owns
          * the write lock (reentrant).
          */
         protected boolean allowReader() {
-            return activeWriter_ == null
-                    || activeWriter_ == Thread.currentThread();
+            return TransactionContext.isCurrentXid(activeXid, (activeWriter_ == null || activeWriter_ == Thread.currentThread()));
+        }
+
+        /**
+         * Sets the active Xid
+         * @param id
+         */
+        synchronized void setActiveXid(Xid xid) {
+            activeXid = xid;
         }
+        
     }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java Wed Jan 14 06:04:17 2009
@@ -29,6 +29,7 @@
 import javax.jcr.Session;
 
 import org.apache.jackrabbit.api.XASession;
+import org.apache.jackrabbit.core.state.TimeBomb;
 
 /**
  * Internal {@link javax.transaction.UserTransaction} implementation.
@@ -55,6 +56,8 @@
      */
     private int status = Status.STATUS_NO_TRANSACTION;
 
+    private boolean distributedThreadAccess = false;
+    
     /**
      * Create a new instance of this class. Takes a session as parameter.
      * @param session session. If session is not of type
@@ -62,8 +65,19 @@
      * is thrown
      */
     public UserTransactionImpl(Session session) {
+        this(session, false);
+    }
+
+    /**
+     * Create a new instance of this class. Takes a session as parameter.
+     * @param session session. If session is not of type
+     * {@link XASession}, an <code>IllegalArgumentException</code>
+     * is thrown
+     */
+    public UserTransactionImpl(Session session, boolean distributedThreadAccess) {
         if (session instanceof XASession) {
             xares = ((XASession) session).getXAResource();
+            this.distributedThreadAccess = distributedThreadAccess; 
         } else {
             throw new IllegalArgumentException("Session not of type XASession");
         }
@@ -108,7 +122,34 @@
             status = Status.STATUS_PREPARED;
 
             status = Status.STATUS_COMMITTING;
-            xares.commit(xid, false);
+            if (distributedThreadAccess) {
+                try {
+                    final Thread t = Thread.currentThread();
+                    final TimeBomb tb = new TimeBomb(100) {
+                        public void explode() {
+                            t.interrupt();
+                        }
+                    };
+                    tb.arm();
+                    Thread distributedThread = new Thread() {
+                        public void run() {
+                            try {
+                                xares.commit(xid, false);
+                                tb.disarm();                
+                            } catch (Exception e) {
+                                throw new RuntimeException(e.getMessage());
+                            }
+                        }
+                    };
+                    distributedThread.start();
+                    Thread.sleep(200);
+                } catch (InterruptedException e) {
+                    throw new SystemException("commit from different thread but same XID must not block");
+                }
+            } else {
+                xares.commit(xid, false);
+            }
+            
             status = Status.STATUS_COMMITTED;
 
         } catch (XAException e) {
@@ -175,7 +216,16 @@
     /**
      * @see javax.transaction.UserTransaction#setTransactionTimeout
      */
-    public void setTransactionTimeout(int seconds) throws SystemException {}
+    public void setTransactionTimeout(int seconds) throws SystemException {
+        try {
+            xares.setTransactionTimeout(seconds);
+        } catch (XAException e) {
+            SystemException se = new SystemException(
+                    "Unable to set the TransactionTiomeout: XA_ERR=" + e.errorCode);
+            se.initCause(e.getCause());
+            throw se;
+        }
+    }
 
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java Wed Jan 14 06:04:17 2009
@@ -1468,4 +1468,37 @@
         // assert: version label known in other session
         nOther.getVersionHistory().getVersionByLabel(versionLabel);
     }
+    
+    /**
+     * Tests two different Threads for prepare and commit in a Transaction
+     */
+    public void testDistributedThreadAccess() throws Exception {
+        // get user transaction object
+        UserTransaction utx = new UserTransactionImpl(superuser, true);
+        //utx.setTransactionTimeout(50);
+        // start transaction
+        utx.begin();
+
+        // add node and save
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixReferenceable);
+        testRootNode.save();
+
+        // assertion: node exists in this session
+        try {
+            superuser.getNodeByUUID(n.getUUID());
+        } catch (ItemNotFoundException e) {
+            fail("New node not visible after save()");
+        }
+
+        // commit
+        utx.commit();
+
+        // assertion: node exists in this session
+        try {
+            superuser.getNodeByUUID(n.getUUID());
+        } catch (ItemNotFoundException e) {
+            fail("Committed node not visible in this session");
+        }
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java?rev=734400&r1=734399&r2=734400&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java Wed Jan 14 06:04:17 2009
@@ -257,58 +257,4 @@
     }
 
     public abstract ISMLocking createISMLocking();
-
-    protected static abstract class TimeBomb {
-
-        private final boolean[] armed = {false};
-
-        private final long millis;
-
-        private Thread timer;
-
-        public TimeBomb(long millis) {
-            this.millis = millis;
-        }
-
-        public void arm() throws InterruptedException {
-            synchronized (armed) {
-                if (armed[0]) {
-                    return;
-                } else {
-                    timer = new Thread(new Runnable() {
-                        public void run() {
-                            synchronized (armed) {
-                                armed[0] = true;
-                                armed.notify();
-                            }
-                            try {
-                                Thread.sleep(millis);
-                                explode();
-                            } catch (InterruptedException e) {
-                                // disarmed
-                            }
-                        }
-                    });
-                    timer.start();
-                }
-            }
-            synchronized (armed) {
-                while (!armed[0]) {
-                    armed.wait();
-                }
-            }
-        }
-
-        public void disarm() throws InterruptedException {
-            synchronized (armed) {
-                if (!armed[0]) {
-                    return;
-                }
-            }
-            timer.interrupt();
-            timer.join();
-        }
-
-        public abstract void explode();
-    }
 }

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TimeBomb.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TimeBomb.java?rev=734400&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TimeBomb.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TimeBomb.java Wed Jan 14 06:04:17 2009
@@ -0,0 +1,58 @@
+/**
+ * 
+ */
+package org.apache.jackrabbit.core.state;
+
+public abstract class TimeBomb {
+
+    private final boolean[] armed = {false};
+
+    private final long millis;
+
+    private Thread timer;
+
+    public TimeBomb(long millis) {
+        this.millis = millis;
+    }
+
+    public void arm() throws InterruptedException {
+        synchronized (armed) {
+            if (armed[0]) {
+                return;
+            } else {
+                timer = new Thread(new Runnable() {
+                    public void run() {
+                        synchronized (armed) {
+                            armed[0] = true;
+                            armed.notify();
+                        }
+                        try {
+                            Thread.sleep(millis);
+                            explode();
+                        } catch (InterruptedException e) {
+                            // disarmed
+                        }
+                    }
+                });
+                timer.start();
+            }
+        }
+        synchronized (armed) {
+            while (!armed[0]) {
+                armed.wait();
+            }
+        }
+    }
+
+    public void disarm() throws InterruptedException {
+        synchronized (armed) {
+            if (!armed[0]) {
+                return;
+            }
+        }
+        timer.interrupt();
+        timer.join();
+    }
+
+    public abstract void explode();
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TimeBomb.java
------------------------------------------------------------------------------
    svn:eol-style = native