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