summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaspar De Groot2011-05-06 01:25:17 (EDT)
committerCaspar De Groot2011-05-06 01:25:17 (EDT)
commit4fa1a741668450b71c596ba85ebe558bc8098a93 (patch)
tree1b5fec3127b26a96cdc53a013fa9df8f282fa41e
parentcad4f21d20a48d975130d861377380cc5ab04f34 (diff)
downloadcdo-4fa1a741668450b71c596ba85ebe558bc8098a93.zip
cdo-4fa1a741668450b71c596ba85ebe558bc8098a93.tar.gz
cdo-4fa1a741668450b71c596ba85ebe558bc8098a93.tar.bz2
[Bug 341995] ConcurrentModificationException on commit while holding a write lock
https://bugs.eclipse.org/bugs/show_bug.cgi?id=341995
-rw-r--r--plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java3
-rw-r--r--plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java27
-rw-r--r--plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java103
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java3
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java3
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java33
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java54
7 files changed, 171 insertions, 55 deletions
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java
index 2b17b37..1286e63 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java
@@ -29,7 +29,6 @@ import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
-import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.common.util.TransportException;
import org.eclipse.emf.cdo.internal.net4j.bundle.OM;
import org.eclipse.emf.cdo.session.CDOSession;
@@ -214,7 +213,7 @@ public class CDOClientProtocol extends SignalProtocol<CDOSession> implements CDO
}
}
- public CDOException lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
+ public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
LockType lockType, long timeout) throws InterruptedException
{
InterruptedException interruptedException = null;
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java
index 6fb2739..ffe3ca7 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java
@@ -12,26 +12,26 @@
package org.eclipse.emf.cdo.internal.net4j.protocol;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
-import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.util.LockTimeoutException;
-import org.eclipse.emf.cdo.util.StaleRevisionLockException;
import org.eclipse.net4j.util.concurrent.IRWLockManager;
import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
+
import java.io.IOException;
import java.util.List;
/**
* @author Eike Stepper, Caspar De Groot
*/
-public class LockObjectsRequest extends CDOClientRequest<CDOException>
+public class LockObjectsRequest extends CDOClientRequest<LockObjectsResult>
{
private int viewID;
@@ -74,18 +74,25 @@ public class LockObjectsRequest extends CDOClientRequest<CDOException>
}
@Override
- protected CDOException confirming(CDODataInput in) throws IOException
+ protected LockObjectsResult confirming(CDODataInput in) throws IOException
{
- boolean success = in.readBoolean();
- if (success)
+ boolean succesful = in.readBoolean();
+ if (succesful)
{
- return null;
+ boolean clientMustWait = in.readBoolean();
+ long requiredTimestamp = CDOBranchPoint.UNSPECIFIED_DATE;
+ if (clientMustWait)
+ {
+ requiredTimestamp = in.readLong();
+ }
+
+ return new LockObjectsResult(true, false, clientMustWait, requiredTimestamp, null);
}
boolean timedOut = in.readBoolean();
if (timedOut)
{
- return new LockTimeoutException();
+ return new LockObjectsResult(false, true, false, 0, null);
}
int nStaleRevisions = in.readInt();
@@ -95,6 +102,6 @@ public class LockObjectsRequest extends CDOClientRequest<CDOException>
staleRevisions[i] = in.readCDORevisionKey();
}
- return new StaleRevisionLockException(staleRevisions);
+ return new LockObjectsResult(false, false, false, 0, staleRevisions);
}
}
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java
index c060960..1e2ad3e 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java
@@ -23,6 +23,7 @@ import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
import org.eclipse.emf.cdo.server.IView;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+import org.eclipse.emf.cdo.spi.server.InternalSession;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
@@ -38,12 +39,16 @@ import java.util.List;
*/
public class LockObjectsIndication extends CDOReadIndication
{
- private List<Object> objectsToBeLocked = new ArrayList<Object>();
-
private List<CDORevisionKey> staleRevisions = new LinkedList<CDORevisionKey>();
private boolean timedOut;
+ private boolean passiveUpdatesEnabled;
+
+ private long requiredTimestamp;
+
+ private boolean staleNoUpdate;
+
public LockObjectsIndication(CDOServerProtocol protocol)
{
super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS);
@@ -52,6 +57,9 @@ public class LockObjectsIndication extends CDOReadIndication
@Override
protected void indicating(CDODataInput in) throws IOException
{
+ InternalSession session = getSession();
+ passiveUpdatesEnabled = session.isPassiveUpdateEnabled();
+
int viewID = in.readInt();
LockType lockType = in.readCDOLockType();
long timeout = in.readLong();
@@ -62,16 +70,24 @@ public class LockObjectsIndication extends CDOReadIndication
for (int i = 0; i < nRevisions; i++)
{
revKeys[i] = in.readCDORevisionKey();
- handleViewedRevision(viewedBranch, revKeys[i]);
}
- if (staleRevisions.size() > 0)
+ List<Object> objectsToBeLocked = new ArrayList<Object>();
+ boolean isSupportingBranches = getRepository().isSupportingBranches();
+ for (CDORevisionKey revKey : revKeys)
{
- // If we have 1 or more stale revisions, we should not lock
- return;
+ CDOID id = revKey.getID();
+ if (isSupportingBranches)
+ {
+ objectsToBeLocked.add(CDOIDUtil.createIDAndBranch(id, viewedBranch));
+ }
+ else
+ {
+ objectsToBeLocked.add(id);
+ }
}
- IView view = getSession().getView(viewID);
+ IView view = session.getView(viewID);
InternalLockManager lockManager = getRepository().getLockManager();
try
{
@@ -80,51 +96,51 @@ public class LockObjectsIndication extends CDOReadIndication
catch (TimeoutRuntimeException ex)
{
timedOut = true;
+ return;
}
catch (InterruptedException ex)
{
throw WrappedException.wrap(ex);
}
- }
- private void handleViewedRevision(CDOBranch viewedBranch, CDORevisionKey revKey)
- {
- CDOID id = revKey.getID();
- InternalCDORevision rev = getRepository().getRevisionManager().getRevision(id, viewedBranch.getHead(),
- CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
-
- if (rev == null)
+ try
{
- throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id,
- viewedBranch));
+ for (CDORevisionKey revKey : revKeys)
+ {
+ checkStale(viewedBranch, revKey);
+ }
}
-
- if (!revKey.equals(rev))
+ catch (IllegalArgumentException ex)
{
- staleRevisions.add(revKey);
-
- // If we have 1 or more stale revisions, the locking won't proceed for sure,
- // so we can return early
- return;
+ lockManager.unlock(lockType, view, objectsToBeLocked);
+ throw ex;
}
- if (getRepository().isSupportingBranches())
- {
- objectsToBeLocked.add(CDOIDUtil.createIDAndBranch(id, viewedBranch));
- }
- else
+ // If some of the clients' revisions are stale and it has passiveUpdates disabled,
+ // then the locks are useless so we release them and report the stale revisions (later)
+ staleNoUpdate = staleRevisions.size() > 0 && !passiveUpdatesEnabled;
+ if (staleNoUpdate)
{
- objectsToBeLocked.add(id);
+ lockManager.unlock(lockType, view, objectsToBeLocked);
}
}
@Override
protected void responding(CDODataOutput out) throws IOException
{
- boolean success = !timedOut && staleRevisions.size() == 0;
- out.writeBoolean(success);
-
- if (!success)
+ boolean lockSuccesful = !timedOut && !staleNoUpdate;
+ out.writeBoolean(lockSuccesful);
+
+ if (lockSuccesful)
+ {
+ boolean clientMustWait = staleRevisions.size() > 0;
+ out.writeBoolean(clientMustWait);
+ if (clientMustWait)
+ {
+ out.writeLong(requiredTimestamp);
+ }
+ }
+ else
{
out.writeBoolean(timedOut);
if (!timedOut)
@@ -137,4 +153,23 @@ public class LockObjectsIndication extends CDOReadIndication
}
}
}
+
+ private void checkStale(CDOBranch viewedBranch, CDORevisionKey revKey)
+ {
+ CDOID id = revKey.getID();
+ InternalCDORevision rev = getRepository().getRevisionManager().getRevision(id, viewedBranch.getHead(),
+ CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
+
+ if (rev == null)
+ {
+ throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id,
+ viewedBranch));
+ }
+
+ if (!revKey.equals(rev))
+ {
+ staleRevisions.add(revKey);
+ requiredTimestamp = Math.max(requiredTimestamp, rev.getTimeStamp());
+ }
+ }
}
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
index c44a3d2..bedcd7f 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java
@@ -30,7 +30,6 @@ import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
-import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.common.util.CDOQueryQueue;
import org.eclipse.emf.cdo.server.StoreThreadLocal;
import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
@@ -325,7 +324,7 @@ public class EmbeddedClientSessionProtocol extends Lifecycle implements CDOSessi
throw new UnsupportedOperationException();
}
- public CDOException lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
+ public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
LockType lockType, long timeout) throws InterruptedException
{
throw new UnsupportedOperationException();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java
index 61c0aae..ed56b53 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java
@@ -28,7 +28,6 @@ import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
-import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.session.CDOSession.ExceptionHandler;
import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
@@ -548,7 +547,7 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr
}
}
- public CDOException lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
+ public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
LockType lockType, long timeout) throws InterruptedException
{
int attempt = 0;
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
index 7396238..27f08bb 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
@@ -29,7 +29,9 @@ import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.emf.cdo.transaction.CDOCommitContext;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.util.CDOUtil;
+import org.eclipse.emf.cdo.util.LockTimeoutException;
import org.eclipse.emf.cdo.util.ReadOnlyException;
+import org.eclipse.emf.cdo.util.StaleRevisionLockException;
import org.eclipse.emf.cdo.view.CDOAdapterPolicy;
import org.eclipse.emf.cdo.view.CDOFeatureAnalyzer;
import org.eclipse.emf.cdo.view.CDOInvalidationPolicy;
@@ -70,6 +72,7 @@ import org.eclipse.emf.common.notify.impl.NotificationImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
+import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult;
import org.eclipse.emf.spi.cdo.FSMUtil;
import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.emf.spi.cdo.InternalCDOSession;
@@ -263,10 +266,34 @@ public class CDOViewImpl extends AbstractCDOView
}
CDOSessionProtocol sessionProtocol = session.getSessionProtocol();
- CDOException exception = sessionProtocol.lockObjects(revisions, viewID, getBranch(), lockType, timeout);
- if (exception != null)
+ LockObjectsResult result = sessionProtocol.lockObjects(revisions, viewID, getBranch(), lockType, timeout);
+
+ if (!result.isSuccessful())
{
- throw exception;
+ if (result.isTimedOut())
+ {
+ throw new LockTimeoutException();
+ }
+
+ CDORevisionKey[] staleRevisions = result.getStaleRevisions();
+ if (staleRevisions != null)
+ {
+ throw new StaleRevisionLockException(staleRevisions);
+ }
+
+ throw new AssertionError("Unexpected lock result state");
+ }
+
+ if (result.isWaitForUpdate())
+ {
+ if (!getSession().options().isPassiveUpdateEnabled())
+ {
+ throw new AssertionError(
+ "Lock result requires client to wait, but client does not have passiveUpdates enabled.");
+ }
+
+ long requiredTimestamp = result.getRequiredTimestamp();
+ getSession().waitForUpdate(requiredTimestamp);
}
}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
index b373b93..cb6bc4a 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
@@ -30,7 +30,6 @@ import org.eclipse.emf.cdo.common.revision.CDOReferenceAdjuster;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
-import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage;
import org.eclipse.emf.cdo.spi.common.CDORawReplicationContext;
@@ -126,7 +125,7 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo
/**
* @since 4.0
*/
- public CDOException lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
+ public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch,
LockType lockType, long timeout) throws InterruptedException;
/**
@@ -707,4 +706,55 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo
}
}
}
+
+ /**
+ * @since 4.0
+ */
+ public static final class LockObjectsResult
+ {
+ private boolean successful;
+
+ private boolean timedOut;
+
+ private boolean waitForUpdate;
+
+ private long requiredTimestamp;
+
+ private CDORevisionKey[] staleRevisions;
+
+ public LockObjectsResult(boolean successful, boolean timedOut, boolean waitForUpdate, long requiredTimestamp,
+ CDORevisionKey[] staleRevisions)
+ {
+ this.successful = successful;
+ this.timedOut = timedOut;
+ this.waitForUpdate = waitForUpdate;
+ this.requiredTimestamp = requiredTimestamp;
+ this.staleRevisions = staleRevisions;
+ }
+
+ public boolean isSuccessful()
+ {
+ return successful;
+ }
+
+ public boolean isTimedOut()
+ {
+ return timedOut;
+ }
+
+ public boolean isWaitForUpdate()
+ {
+ return waitForUpdate;
+ }
+
+ public long getRequiredTimestamp()
+ {
+ return requiredTimestamp;
+ }
+
+ public CDORevisionKey[] getStaleRevisions()
+ {
+ return staleRevisions;
+ }
+ }
}