You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2010/11/24 14:59:16 UTC
svn commit: r1038604 - in /jackrabbit/branches/2.0: ./
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/
jackrabbi...
Author: jukka
Date: Wed Nov 24 13:59:16 2010
New Revision: 1038604
URL: http://svn.apache.org/viewvc?rev=1038604&view=rev
Log:
2.0: Merged revision 1038594 (JCR-2573)
Added:
jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/DefaultISMLockingDeadlockTest.java
- copied unchanged from r1038594, jackrabbit/branches/2.1/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/DefaultISMLockingDeadlockTest.java
Modified:
jackrabbit/branches/2.0/ (props changed)
jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/InternalVersionManagerImpl.java
jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TestAll.java
Propchange: jackrabbit/branches/2.0/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Nov 24 13:59:16 2010
@@ -1,6 +1,6 @@
/jackrabbit/branches/1.5:794012,794100,794102
-/jackrabbit/branches/2.1:955309,955314,982266,982277,982505,998310,1025933,1025957,1025962,1025964,1025981,1025985,1025990,1025995,1025999,1026002
+/jackrabbit/branches/2.1:955309,955314,982266,982277,982505,998310,1025933,1025957,1025962,1025964,1025981,1025985,1025990,1025995,1025999,1026002,1038594
/jackrabbit/sandbox/JCR-1456:774917-886178
/jackrabbit/sandbox/JCR-2170:812417-816332
/jackrabbit/sandbox/tripod-JCR-2209:795441-795863
-/jackrabbit/trunk:891595,891629,892253,892263,894150-894151,896408,896513,896532,896857,896870,896876,896908,896940,896942-896943,896969,896977,897071,897836,897842,897858,897935,897983,897992-897993,897996,898002,898042,898267,898325,898540,898677,898699,898701,898715,898872,899102,899181,899391,899393-899394,899583,899594,899643,900305,900310,900314,900453,900702,900736,900762-900763,900767,900782,901095,901122,901139,901144,901170,901176,901191,901193,901196,901216,901228,901285,902058,902062,926324,928888,936668,955222,955229,955307,955852,965539,996810,1001707,1002065-1002066,1002084,1002101-1002102,1002168,1002170,1002589,1002608,1002657,1002729,1003423,1003470,1003542,1003773,1004182,1004184,1004223-1004224,1004652,1005057,1005112
+/jackrabbit/trunk:891595,891629,892253,892263,894150-894151,896408,896513,896532,896857,896870,896876,896908,896940,896942-896943,896969,896977,897071,897836,897842,897858,897935,897983,897992-897993,897996,898002,898042,898267,898325,898540,898677,898699,898701,898715,898872,899102,899181,899391,899393-899394,899583,899594,899643,900305,900310,900314,900453,900702,900736,900762-900763,900767,900782,901095,901122,901139,901144,901170,901176,901191,901193,901196,901216,901228,901285,902058,902062,926324,928888,936668,955222,955229,955307,955852,965539,995411-995412,996810,999298-999299,999965,1000947,1001707,1002065-1002066,1002084,1002101-1002102,1002168,1002170,1002589,1002608,1002657,1002729,1003423,1003470,1003542,1003773,1004182,1004184,1004223-1004224,1004652,1005057,1005112,1036336-1036337
Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java?rev=1038604&r1=1038603&r2=1038604&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransactionContext.java Wed Nov 24 13:59:16 2010
@@ -352,4 +352,36 @@ public class TransactionContext extends
Xid currentXid = CURRENT_XID.get();
return fallback ? true : (currentXid == null || xid == null) ? fallback : Arrays.equals(xid.getGlobalTransactionId(), currentXid.getGlobalTransactionId());
}
+
+ /**
+ * Returns the current thread identifier. The identifier is either the
+ * current thread instance or the global transaction identifier when
+ * running under a transaction.
+ *
+ * @return current thread identifier
+ */
+ public static Object getCurrentThreadId() {
+ Xid xid = TransactionContext.getCurrentXid();
+ if (xid != null) {
+ return xid.getGlobalTransactionId();
+ } else {
+ return Thread.currentThread();
+ }
+ }
+
+ /**
+ * Compares the given thread identifiers for equality.
+ *
+ * @see #getCurrentThreadId()
+ */
+ public static boolean isSameThreadId(Object a, Object b) {
+ if (a == b) {
+ return true;
+ } else if (a instanceof byte[] && b instanceof byte[]) {
+ return Arrays.equals((byte[]) a, (byte[]) b);
+ } else {
+ return false;
+ }
+ }
+
}
Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java?rev=1038604&r1=1038603&r2=1038604&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java Wed Nov 24 13:59:16 2010
@@ -16,137 +16,143 @@
*/
package org.apache.jackrabbit.core.state;
-import java.util.Arrays;
-
-import javax.transaction.xa.Xid;
+import static org.apache.jackrabbit.core.TransactionContext.getCurrentThreadId;
+import static org.apache.jackrabbit.core.TransactionContext.isSameThreadId;
import org.apache.jackrabbit.core.id.ItemId;
import org.apache.jackrabbit.core.TransactionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
-import EDU.oswego.cs.dl.util.concurrent.Sync;
-
/**
- * <code>DefaultISMLocking</code> implements the default locking strategy using
- * coarse grained locking on an ItemStateManager wide read-write lock. E.g.
- * while a write lock is held, no read lock can be acquired.
+ * Default item state locking strategy. The default strategy is simply to use
+ * a single coarse-grained read-write lock over the entire workspace.
*/
public class DefaultISMLocking implements ISMLocking {
/**
- * Logger instance
+ * The read lock instance used by readers to release the acquired lock.
*/
- private static final Logger log = LoggerFactory.getLogger(DefaultISMLocking.class);
+ private final ReadLock readLock = new ReadLock() {
+ public void release() {
+ releaseReadLock();
+ }
+ };
/**
- * The internal read-write lock.
+ * The write lock instance used by writers to release or downgrade the
+ * acquired lock.
*/
- private final RWLock rwLock = new RWLock();
+ private final WriteLock writeLock = new WriteLock() {
+ public void release() {
+ releaseWriteLock(false);
+ }
+ public ReadLock downgrade() {
+ releaseWriteLock(true);
+ return readLock;
+ }
+ };
/**
- * {@inheritDoc}
+ * Number of writer threads waiting. While greater than zero, no new
+ * (unrelated) readers are allowed to proceed.
*/
- public ReadLock acquireReadLock(ItemId id)
- throws InterruptedException {
- return new ReadLockImpl(rwLock.readLock());
- }
+ private int writersWaiting = 0;
/**
- * {@inheritDoc}
+ * The thread identifier of the current writer, or <code>null</code> if
+ * no write is in progress. A thread with the same identifier (i.e. the
+ * same thread or another thread in the same transaction) can re-acquire
+ * read or write locks without limitation, while all other readers and
+ * writers remain blocked. Note that a downgraded write lock still retains
+ * the writer thread identifier, which allows related threads to reacquire
+ * read or write locks even when there are concurrent writers waiting.
*/
- public WriteLock acquireWriteLock(ChangeLog changeLog)
- throws InterruptedException {
- return new WriteLock() {
+ private Object writerId = null;
- {
- rwLock.writeLock().acquire();
- rwLock.setActiveXid(TransactionContext.getCurrentXid());
- }
+ /**
+ * Number of acquired write locks. All the concurrent write locks are
+ * guaranteed to share the same thread identifier (see {@link #writerId}).
+ */
+ private int writerCount = 0;
- /**
- * {@inheritDoc}
- */
- public void release() {
- rwLock.writeLock().release();
- }
+ /**
+ * Number of acquired read locks.
+ */
+ private int readerCount = 0;
- /**
- * {@inheritDoc}
- */
- public ReadLock downgrade() throws InterruptedException {
- ReadLock rLock = new ReadLockImpl(rwLock.readLock());
- release();
- return rLock;
- }
- };
+ /**
+ * Increments the reader count and returns the acquired read lock once
+ * there are no more writers or the current writer shares the thread id
+ * with this reader.
+ */
+ public synchronized ReadLock acquireReadLock(ItemId id)
+ throws InterruptedException {
+ Object currentId = getCurrentThreadId();
+ while (writerId != null
+ ? (writerCount > 0 && !isSameThreadId(writerId, currentId))
+ : writersWaiting > 0) {
+ wait();
+ }
+
+ readerCount++;
+ return readLock;
}
- private static final class ReadLockImpl implements ReadLock {
+ /**
+ * Decrements the reader count and notifies all pending threads if the
+ * lock is now available. Used by the {@link #readLock} instance.
+ */
+ private synchronized void releaseReadLock() {
+ readerCount--;
+ if (readerCount == 0 && writerCount == 0) {
+ writerId = null;
+ notifyAll();
+ }
+ }
- private final Sync readLock;
+ /**
+ * Increments the writer count, sets the writer identifier and returns
+ * the acquired read lock once there are no other active readers or
+ * writers or the current writer shares the thread id with this writer.
+ */
+ public synchronized WriteLock acquireWriteLock(ChangeLog changeLog)
+ throws InterruptedException {
+ Object currentId = getCurrentThreadId();
- private ReadLockImpl(Sync readLock) throws InterruptedException {
- this.readLock = readLock;
- this.readLock.acquire();
+ writersWaiting++;
+ try {
+ while (writerId != null
+ ? !isSameThreadId(writerId, currentId) : readerCount > 0) {
+ wait();
+ }
+ } finally {
+ writersWaiting--;
}
- /**
- * {@inheritDoc}
- */
- public void release() {
- readLock.release();
+ if (writerCount++ == 0) {
+ writerId = currentId;
}
+ return writeLock;
}
- 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 TransactionContext.isCurrentXid(activeXid, (activeWriter_ == null || activeWriter_ == Thread.currentThread()));
- }
-
- /**
- * Sets the active Xid
- * @param xid
- */
- synchronized void setActiveXid(Xid xid) {
- if (activeXid != null && xid != null) {
- boolean sameGTI = Arrays.equals(activeXid.getGlobalTransactionId(), xid.getGlobalTransactionId());
- if (!sameGTI) {
- log.warn("Unable to set the ActiveXid while a other one is associated with a different GloalTransactionId with this RWLock.");
- return;
- }
- }
- activeXid = xid;
+ /**
+ * Decrements the writer count (and possibly clears the writer identifier)
+ * and notifies all pending threads if the lock is now available. If the
+ * downgrade argument is true, then the reader count is incremented before
+ * notifying any pending threads. Used by the {@link #writeLock} instance.
+ */
+ private synchronized void releaseWriteLock(boolean downgrade) {
+ writerCount--;
+ if (downgrade) {
+ readerCount++;
}
-
- /**
- * {@inheritDoc}
- *
- * If there are no more writeHolds the activeXid will be set to null
- */
- protected synchronized Signaller endWrite() {
- --writeHolds_;
- if (writeHolds_ > 0) { // still being held
- return null;
- } else {
- activeXid = null;
- activeWriter_ = null;
- if (waitingReaders_ > 0 && allowReader()) {
- return readerLock_;
- } else if (waitingWriters_ > 0) {
- return writerLock_;
- } else {
- return null;
- }
+ if (writerCount == 0) {
+ if (readerCount == 0) {
+ writerId = null;
}
+ notifyAll();
}
}
+
}
Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java?rev=1038604&r1=1038603&r2=1038604&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java Wed Nov 24 13:59:16 2010
@@ -39,6 +39,12 @@ import org.apache.jackrabbit.core.id.Ite
* is free to block requests entirely for additional write lock while a write
* lock is active. It is not a requirement to support concurrent write locks.
* </li>
+ * <li>While a write lock is held for a change log <code>C</code>, the holder
+ * of the write lock (and any related threads) needs to be able to acquire
+ * a read lock even if other writers are waiting for the lock. This behaviour
+ * must continue also when the write lock has been downgraded. Note that it
+ * is not necessary for a holder of a read lock to be able to upgrade to a
+ * write lock.</li>
* </ul>
*/
public interface ISMLocking {
@@ -81,10 +87,8 @@ public interface ISMLocking {
* used to further release the read lock.
*
* @return the read lock downgraded from this write lock.
- * @throws InterruptedException if the current thread is interrupted
- * while downgrading the write lock.
*/
- ReadLock downgrade() throws InterruptedException;
+ ReadLock downgrade();
}
Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java?rev=1038604&r1=1038603&r2=1038604&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java Wed Nov 24 13:59:16 2010
@@ -790,9 +790,6 @@ public class SharedItemStateManager
String path = events.getSession().getUserID() + "@" + events.getCommonPath();
eventChannel.updateCommitted(this, path);
}
-
- } catch (InterruptedException e) {
- throw new ItemStateException("Interrupted while downgrading to read lock");
} finally {
if (writeLock != null) {
// exception occurred before downgrading lock
@@ -1515,9 +1512,6 @@ public class SharedItemStateManager
holdingWriteLock = false;
events.dispatch();
}
- } catch (InterruptedException e) {
- String msg = "Unable to downgrade to read lock.";
- log.error(msg);
} finally {
if (holdingWriteLock) {
if (wLock != null) {
Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/InternalVersionManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/InternalVersionManagerImpl.java?rev=1038604&r1=1038603&r2=1038604&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/InternalVersionManagerImpl.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/InternalVersionManagerImpl.java Wed Nov 24 13:59:16 2010
@@ -29,7 +29,6 @@ import javax.jcr.version.VersionExceptio
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.RepositoryImpl;
import org.apache.jackrabbit.core.cluster.UpdateEventChannel;
import org.apache.jackrabbit.core.cluster.UpdateEventListener;
import org.apache.jackrabbit.core.fs.FileSystem;
@@ -58,9 +57,9 @@ import org.apache.jackrabbit.core.value.
import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.apache.jackrabbit.spi.PathFactory;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
-import org.apache.jackrabbit.spi.commons.name.PathBuilder;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,38 +79,13 @@ public class InternalVersionManagerImpl
*/
private static final Path SYSTEM_PATH;
- /**
- * The path to the version storage: /jcr:system/jcr:versionStorage
- */
- private static final Path HISTORIES_PATH;
-
- /**
- * The path to the version storage: /jcr:system/jcr:versionStorage/jcr:activities
- */
- private static final Path ACTIVITIES_PATH;
-
static {
try {
- PathBuilder builder = new PathBuilder();
- builder.addRoot();
- builder.addLast(NameConstants.JCR_SYSTEM);
- SYSTEM_PATH = builder.getPath();
-
- builder = new PathBuilder();
- builder.addRoot();
- builder.addLast(NameConstants.JCR_SYSTEM);
- builder.addLast(NameConstants.JCR_VERSIONSTORAGE);
- HISTORIES_PATH = builder.getPath();
-
- builder = new PathBuilder();
- builder.addRoot();
- builder.addLast(NameConstants.JCR_SYSTEM);
- builder.addLast(NameConstants.JCR_ACTIVITIES);
- ACTIVITIES_PATH = builder.getPath();
-
- } catch (MalformedPathException e) {
- // will not happen. path is always valid
- throw new InternalError("Cannot initialize path");
+ PathFactory factory = PathFactoryImpl.getInstance();
+ SYSTEM_PATH = factory.create(
+ factory.getRootPath(), NameConstants.JCR_SYSTEM, true);
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
}
}
@@ -185,28 +159,6 @@ public class InternalVersionManagerImpl
this.fs = fs;
this.escFactory = new DynamicESCFactory(obsMgr);
- // need to recreate the jcr:system node in this pm, too. so that
- // it can act as parent for the histories and activities.
- if (false && !pMgr.exists(systemId)) {
- NodeState root = pMgr.createNew(systemId);
- root.setParentId(RepositoryImpl.ROOT_NODE_ID);
- root.setNodeTypeName(NameConstants.REP_SYSTEM);
- PropertyState pt = pMgr.createNew(new PropertyId(systemId, NameConstants.JCR_PRIMARYTYPE));
- pt.setMultiValued(false);
- pt.setType(PropertyType.NAME);
- pt.setValues(new InternalValue[]{InternalValue.create(NameConstants.REP_SYSTEM)});
- root.addPropertyName(pt.getName());
-
- // add version storage and activities as child node entries
- root.addChildNodeEntry(NameConstants.JCR_VERSIONSTORAGE, historiesId);
- root.addChildNodeEntry(NameConstants.JCR_ACTIVITIES, activitiesId);
-
- ChangeLog cl = new ChangeLog();
- cl.added(root);
- cl.added(pt);
- pMgr.store(cl);
- }
-
// need to store the version storage root directly into the persistence manager
if (!pMgr.exists(historiesId)) {
NodeState root = pMgr.createNew(historiesId);
@@ -701,13 +653,13 @@ public class InternalVersionManagerImpl
/**
* the observation manager
*/
- private DelegatingObservationDispatcher obsMgr;
+ private final DelegatingObservationDispatcher obsMgr;
/**
- * the current event source
+ * The event source of the current thread.
*/
- private SessionImpl source;
-
+ private final ThreadLocal<SessionImpl> source =
+ new ThreadLocal<SessionImpl>();
/**
* Creates a new event state collection factory
@@ -725,12 +677,14 @@ public class InternalVersionManagerImpl
* association between update operation and session who actually invoked
* the update, an internal event source is used.
*/
- public synchronized EventStateCollection createEventStateCollection()
+ public EventStateCollection createEventStateCollection()
throws RepositoryException {
- if (source == null) {
+ SessionImpl session = source.get();
+ if (session != null) {
+ return createEventStateCollection(session);
+ } else {
throw new RepositoryException("Unknown event source.");
}
- return createEventStateCollection(source);
}
/**
@@ -753,15 +707,16 @@ public class InternalVersionManagerImpl
* @return the return value of the executed runnable
* @throws RepositoryException if an error occurs
*/
- public synchronized Object doSourced(SessionImpl eventSource, SourcedTarget runnable)
+ public Object doSourced(SessionImpl eventSource, SourcedTarget runnable)
throws RepositoryException {
- this.source = eventSource;
+ source.set(eventSource);
try {
return runnable.run();
} finally {
- this.source = null;
+ source.remove();
}
}
+
}
private abstract class SourcedTarget {
Modified: jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TestAll.java?rev=1038604&r1=1038603&r2=1038604&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TestAll.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/TestAll.java Wed Nov 24 13:59:16 2010
@@ -37,6 +37,7 @@ public class TestAll extends TestCase {
suite.addTestSuite(ChangeLogTest.class);
suite.addTestSuite(DefaultISMLockingTest.class);
+ suite.addTestSuite(DefaultISMLockingDeadlockTest.class);
suite.addTestSuite(FineGrainedISMLockingTest.class);
suite.addTestSuite(NameSetTest.class);