summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaspar De Groot2011-07-26 06:20:00 (EDT)
committerCaspar De Groot2011-07-26 06:20:00 (EDT)
commita446533db04677db8686a47de128143ffd6c52f3 (patch)
treed4be33c0cdf37ed8c94755f5c21ae47acbd0956d
parent22a22321c1ba1d08f3590bfb8723a6c8797848a1 (diff)
downloadcdo-a446533db04677db8686a47de128143ffd6c52f3.zip
cdo-a446533db04677db8686a47de128143ffd6c52f3.tar.gz
cdo-a446533db04677db8686a47de128143ffd6c52f3.tar.bz2
[351793] Enhance LockMgr with write options
https://bugs.eclipse.org/bugs/show_bug.cgi?id=351793
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java57
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java10
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java21
3 files changed, 76 insertions, 12 deletions
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java
index 798b000..f799883 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java
@@ -28,6 +28,7 @@ import org.eclipse.emf.cdo.util.StaleRevisionLockException;
import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
import org.eclipse.net4j.util.concurrent.RWOLockManager;
+import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException;
import org.eclipse.net4j.util.io.IOUtil;
import java.util.ArrayList;
@@ -50,7 +51,7 @@ public class LockingManagerTest extends AbstractLockingTest
Set<Integer> keys = new HashSet<Integer>();
keys.add(1);
- lockingManager.lock(LockType.OPTION, 1, keys, 1000);
+ lockingManager.lock(LockType.OPTION, 1, keys, 100);
// (R=Read, W=Write, WO=WriteOption)
// Scenario 1: 1 has WO, 2 requests W -> fail
@@ -59,19 +60,19 @@ public class LockingManagerTest extends AbstractLockingTest
try
{
- lockingManager.lock(LockType.WRITE, 2, keys, 1000); // Must fail
+ lockingManager.lock(LockType.WRITE, 2, keys, 100); // Must fail
fail("Should have thrown an exception");
}
- catch (Exception e)
+ catch (TimeoutRuntimeException e)
{
}
// Scenario 2: 1 has WO, 2 requests R -> succeed
try
{
- lockingManager.lock(LockType.READ, 2, keys, 1000); // Must succeed
+ lockingManager.lock(LockType.READ, 2, keys, 100); // Must succeed
}
- catch (Exception e)
+ catch (TimeoutRuntimeException e)
{
fail("Should not have thrown an exception");
}
@@ -79,20 +80,20 @@ public class LockingManagerTest extends AbstractLockingTest
// Scenario 3: 1 has WO, 2 has R, 1 requests W -> fail
try
{
- lockingManager.lock(LockType.WRITE, 1, keys, 1000); // Must fail
+ lockingManager.lock(LockType.WRITE, 1, keys, 100); // Must fail
fail("Should have thrown an exception");
}
- catch (Exception e)
+ catch (TimeoutRuntimeException e)
{
}
// Scenario 4: 1 has WO, 2 has R, 2 requests WO -> fail
try
{
- lockingManager.lock(LockType.OPTION, 2, keys, 1000); // Must fail
+ lockingManager.lock(LockType.OPTION, 2, keys, 100); // Must fail
fail("Should have thrown an exception");
}
- catch (Exception e)
+ catch (TimeoutRuntimeException e)
{
}
@@ -100,12 +101,46 @@ public class LockingManagerTest extends AbstractLockingTest
lockingManager.unlock(LockType.READ, 2, keys);
try
{
- lockingManager.lock(LockType.OPTION, 2, keys, 1000); // Must fail
+ lockingManager.lock(LockType.OPTION, 2, keys, 100); // Must fail
fail("Should have thrown an exception");
}
- catch (Exception e)
+ catch (TimeoutRuntimeException e)
{
}
+
+ // Scenario 6: 1 has W, 2 has nothing, 2 requests WO -> fail
+ lockingManager.unlock(LockType.OPTION, 1, keys);
+ lockingManager.lock(LockType.WRITE, 1, keys, 100);
+ try
+ {
+ lockingManager.lock(LockType.OPTION, 2, keys, 100); // Must fail
+ fail("Should have thrown an exception");
+ }
+ catch (TimeoutRuntimeException e)
+ {
+ }
+
+ // Scenario 7: 1 has W, 1 request WO -> succeed
+ try
+ {
+ lockingManager.lock(LockType.OPTION, 1, keys, 100); // Must succeed
+ }
+ catch (TimeoutRuntimeException e)
+ {
+ fail("Should not have thrown an exception");
+ }
+
+ // Scenario 8: 1 has W, 2 has R, 1 request WO -> succeed
+ lockingManager.unlock(LockType.OPTION, 1, keys);
+ lockingManager.lock(LockType.READ, 1, keys, 100);
+ try
+ {
+ lockingManager.lock(LockType.OPTION, 1, keys, 100); // Must succeed
+ }
+ catch (TimeoutRuntimeException e)
+ {
+ fail("Should not have thrown an exception");
+ }
}
public void testBasicUpgradeFromReadToWriteLock() throws Exception
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java
index 16dcd90..24e65bf 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java
@@ -126,6 +126,16 @@ public interface CDOObject extends EObject, CDOWithID
public CDOLock cdoWriteLock();
/**
+ * Returns the write option associated with this object.
+ * <p>
+ * A write option is a lock that
+ * <li>is exclusive; i.e. can only be held by one view</li>
+ * <li>prevents other views from obtaining a write lock on the same object</li>
+ * <li>does not prevent other views from obtaining a read lock on the same object</li>
+ * <p>
+ * It thus allows a view to ensure that it is the only that who will be able to obtain a write lock in the future,
+ * without preventing read locks to be obtained by others at this moment.
+ *
* @since 4.1
*/
public CDOLock cdoWriteOption();
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java
index 48a8643..d61dd27 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java
@@ -26,6 +26,10 @@ import java.util.Map;
import java.util.Set;
/**
+ * Keeps track of locks on objects. Locks are owned by contexts. A particular combination of locks and their owners, for
+ * a given object, is represented by instances of the {@link LockState} class. This class is also repsonsible for
+ * deciding whether or not a new lock can be granted, based on the locks already present.
+ *
* @author Caspar De Groot
* @since 3.2
*/
@@ -284,7 +288,17 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc
}
/**
- * Represents a combination of locks for one OBJECT.
+ * Represents a combination of locks for one OBJECT. The different lock types are represented by the values of the
+ * enum {@link LockType}.
+ * <p>
+ * The locking semantics established by this class are as follows:
+ * <li>a read lock prevents a write lock by another, but allows read locks by others and allows a write option by
+ * another, and is therefore <b>non-exclusive</b></li>
+ * <li>a write lock prevents read locks by others, a write lock by another, and a write option by another, and is
+ * therefore <b>exclusive</b></li>
+ * <li>a write option prevents write locks by others and a write option by another, but allows read locks by others,
+ * and is therefore <b>exclusive</b></li>
+ * <p>
*
* @author Caspar De Groot
* @since 3.2
@@ -561,6 +575,11 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc
return false;
}
+ if (writeLockOwner != null && writeLockOwner != context)
+ {
+ return false;
+ }
+
return true;
}