diff options
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 798b00081a..f799883e4e 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 16dcd90fd9..24e65bfe08 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 48a8643252..d61dd27ee2 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; } |