/** * Constructs a new <code>MutuallyExclusiveSetLock</code> that will * lock the objects in the order determined by <code>comparator</code>. * @param fair when <code>true</code>, the class favors granting access to the * longest-waiting thread when there is any contention. * When <code>false</code>, no access order is guaranteed. * @param comparator a <code>java.util.Comparator</code> to use in determining lock order. */ public static <T> MutuallyExclusiveSetLock<T> createWithComparator(boolean fair, Comparator<? super T> comparator) { return new MutuallyExclusiveSetLock<T>(fair, comparator); }
private Thread createThread(final MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock, final Collection<String> toLock) { final Thread thread; thread = new Thread(() -> { LockState<String> locked = mutuallyExclusiveSetLock.lockOnObjects(toLock); try { //stuff } finally { mutuallyExclusiveSetLock.unlock(locked); } }); return thread; } }
/** * Attempts to acquire the locks in increasing order and may block. * * <p>Be sure that the <code>Comparator<T></code> or <code>T.compareTo()</code> * is consistent with <code>T.equals()</code>. You can only lock once on a thread * with a set of objects. If you wish to lock on more objects, * you must unlock then pass the new set of objects to be locked. * @return <code>LockState</code> instance with the information required to * unlock these same objects. * @see #unlock(LockState) */ public LockState<T> lockOnObjects(Iterable<T> lockObjects) { ImmutableSet<T> hashSet = validateLockInput(lockObjects); final SortedMap<T, ReentrantLock> sortedLocks = getSortedLocks(hashSet); for (ReentrantLock lock : sortedLocks.values()) { lock.lock(); } threadSet.add(Thread.currentThread()); return new LockState<T>(sortedLocks.values(), this); }
@Test public void testInterface() throws Exception { MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock = new MutuallyExclusiveSetLock<String>(); LockState<String> lockOnObjects = mutuallyExclusiveSetLock.lockOnObjects(Arrays.asList("whatev", "dog")); try { //stuff } finally { // assertEquals(2, mutuallyExclusiveSetLock.syncMap.size()); // assertEquals(1, mutuallyExclusiveSetLock.threadSet.size()); mutuallyExclusiveSetLock.unlock(lockOnObjects); // assertFalse(mutuallyExclusiveSetLock.syncMap.get("whatev").isHeldByCurrentThread()); // assertEquals(0, mutuallyExclusiveSetLock.threadSet.size()); } }
@Test public void testThreadOwnsLocks() { final MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock = new MutuallyExclusiveSetLock<String>(); List<String> asList = Arrays.asList("whatup", "dog"); LockState<String> lockOnObjects = mutuallyExclusiveSetLock.lockOnObjects(asList); try { Assert.assertTrue(mutuallyExclusiveSetLock.isLocked(asList)); } finally { mutuallyExclusiveSetLock.unlock(lockOnObjects); } }
@Test public void testThreadDoesNotOwnUnlocked() { final MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock = new MutuallyExclusiveSetLock<String>(); List<String> asList = Arrays.asList("whatup", "dog"); Assert.assertTrue(!mutuallyExclusiveSetLock.isLocked(asList)); }
/** * Unlocks the objects acquired from locking. * This method should always be in a try/finally block immediately after the lock. * If you try to unlock from another thread, no objects are unlocked. * @see #lockOnObjects(Iterable) */ public void unlock() { setLock.unlock(this); } }
@Override public void putUnlessExists(TableReference tableRef, Map<Cell, byte[]> values) throws KeyAlreadyExistsException { LockState<Cell> locks = lockSet.lockOnObjects(values.keySet()); try (Disposer d = new Disposer(); ColumnFamily table = columnFamilies.get(tableRef.getQualifiedName())) { Set<Cell> alreadyExists = Sets.newHashSetWithExpectedSize(0); WriteOptions options = d.register(new WriteOptions().setSync(writeOptions.fsyncCommit())); WriteBatch batch = d.register(new WriteBatch()); RocksIterator iter = d.register(getDb().newIterator(table.getHandle())); for (Entry<Cell, byte[]> entry : values.entrySet()) { byte[] key = RocksDbKeyValueServices.getKey(entry.getKey(), PUT_UNLESS_EXISTS_TS); if (RocksDbKeyValueServices.keyExists(iter, key)) { alreadyExists.add(entry.getKey()); } else { batch.put(table.getHandle(), key, entry.getValue()); } } getDb().write(options, batch); if (!alreadyExists.isEmpty()) { throw new KeyAlreadyExistsException("key already exists", alreadyExists); } } catch (RocksDBException e) { throw Throwables.propagate(e); } finally { locks.unlock(); } }
@Test public void testDoubleLock() throws Exception { final MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock = new MutuallyExclusiveSetLock<String>(); LockState<String> lockOnObjects = mutuallyExclusiveSetLock.lockOnObjects(Arrays.asList("whatup", "dog")); try { mutuallyExclusiveSetLock.lockOnObjects(Arrays.asList("anything")); } catch(Exception e) { return; //expected } finally { mutuallyExclusiveSetLock.unlock(lockOnObjects); } Assert.fail(); //should have thrown }
@Test public void testThreadDoesNotOwnOtherLocked() { final MutuallyExclusiveSetLock<String> setLock = new MutuallyExclusiveSetLock<String>(); final List<String> toLock = Arrays.asList("whatup", "dog"); LockState<String> locked = setLock.lockOnObjects(toLock); try { while (!unlock) { setLock.unlock(locked); try { Thread.sleep(1000); Assert.assertFalse("locks should be held by other thread", setLock.isLocked(toLock)); unlock = true; locker.join(); Assert.fail("unexpected interruption: " + e); Assert.assertFalse("locks should be held by other thread", setLock.isLocked(toLock));
/** * Unlocks the objects acquired from locking. * This method should always be in a try/finally block immediately after the lock. * If you try to unlock from another thread, no objects are unlocked. * @see #lockOnObjects(Iterable) */ public void unlock() { setLock.unlock(this); } }
@Test public void testSimpleNotBlock() throws Exception { final MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock = new MutuallyExclusiveSetLock<String>(); LockState<String> lockOnObjects = mutuallyExclusiveSetLock.lockOnObjects(Arrays.asList("whatup", "dog")); final Thread thread; try { thread = createThread(mutuallyExclusiveSetLock, Arrays.asList("heyo")); thread.setDaemon(true); thread.start(); thread.join(10 * 1000); } finally { mutuallyExclusiveSetLock.unlock(lockOnObjects); } }
public LockState<T> lockOnObjectsInterruptibly(Iterable<T> lockObjects) throws InterruptedException { ImmutableSet<T> hashSet = validateLockInput(lockObjects); List<ReentrantLock> toUnlock = Lists.newArrayList(); try { final SortedMap<T, ReentrantLock> sortedLocks = getSortedLocks(hashSet); for (ReentrantLock lock : sortedLocks.values()) { lock.lockInterruptibly(); toUnlock.add(lock); } LockState<T> ret = new LockState<T>(sortedLocks.values(), this); threadSet.add(Thread.currentThread()); toUnlock.clear(); return ret; } finally { for (ReentrantLock reentrantLock : toUnlock) { reentrantLock.unlock(); } } }
public static <T extends Comparable<? super T>> MutuallyExclusiveSetLock<T> create(boolean fair) { return new MutuallyExclusiveSetLock<T>(fair); }
@Test public void testSimpleBlock() throws Exception { final MutuallyExclusiveSetLock<String> mutuallyExclusiveSetLock = new MutuallyExclusiveSetLock<String>(); LockState<String> lockOnObjects = mutuallyExclusiveSetLock.lockOnObjects(Arrays.asList("whatup", "dog")); final Thread thread; try { thread = createThread(mutuallyExclusiveSetLock, Arrays.asList("whatup")); thread.setDaemon(true); thread.start(); Thread.sleep(100); Assert.assertTrue(thread.isAlive()); // assertEquals(2, mutuallyExclusiveSetLock.syncMap.size()); } finally { mutuallyExclusiveSetLock.unlock(lockOnObjects); } thread.join(10 * 1000); // assertFalse(mutuallyExclusiveSetLock.syncMap.get("dog").isLocked()); // assertEquals(0, mutuallyExclusiveSetLock.threadSet.size()); }
/** * Attempts to acquire the locks in increasing order and may block. * * <p>Be sure that the <code>Comparator<T></code> or <code>T.compareTo()</code> * is consistent with <code>T.equals()</code>. You can only lock once on a thread * with a set of objects. If you wish to lock on more objects, * you must unlock then pass the new set of objects to be locked. * @return <code>LockState</code> instance with the information required to * unlock these same objects. * @see #unlock(LockState) */ public LockState<T> lockOnObjects(Iterable<T> lockObjects) { ImmutableSet<T> hashSet = validateLockInput(lockObjects); final SortedMap<T, ReentrantLock> sortedLocks = getSortedLocks(hashSet); for (ReentrantLock lock : sortedLocks.values()) { lock.lock(); } threadSet.add(Thread.currentThread()); return new LockState<T>(sortedLocks.values(), this); }
/** * Constructs a new <code>MutuallyExclusiveSetLock</code> that will * lock the objects in the order determined by <code>comparator</code>. * @param fair when <code>true</code>, the class favors granting access to the * longest-waiting thread when there is any contention. * When <code>false</code>, no access order is guaranteed. * @param comparator a <code>java.util.Comparator</code> to use in determining lock order. */ public static <T> MutuallyExclusiveSetLock<T> createWithComparator(boolean fair, Comparator<? super T> comparator) { return new MutuallyExclusiveSetLock<T>(fair, comparator); }
public LockState<T> lockOnObjectsInterruptibly(Iterable<T> lockObjects) throws InterruptedException { ImmutableSet<T> hashSet = validateLockInput(lockObjects); List<ReentrantLock> toUnlock = Lists.newArrayList(); try { final SortedMap<T, ReentrantLock> sortedLocks = getSortedLocks(hashSet); for (ReentrantLock lock : sortedLocks.values()) { lock.lockInterruptibly(); toUnlock.add(lock); } LockState<T> ret = new LockState<T>(sortedLocks.values(), this); threadSet.add(Thread.currentThread()); toUnlock.clear(); return ret; } finally { for (ReentrantLock reentrantLock : toUnlock) { reentrantLock.unlock(); } } }
public static <T extends Comparable<? super T>> MutuallyExclusiveSetLock<T> create(boolean fair) { return new MutuallyExclusiveSetLock<T>(fair); }