package org.infinispan.lock;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.infinispan.test.AbstractCacheTest;
import org.infinispan.test.AbstractInfinispanTest;
import org.infinispan.util.concurrent.TimeoutException;
import org.infinispan.util.concurrent.locks.DeadlockDetectedException;
import org.infinispan.util.concurrent.locks.ExtendedLockPromise;
import org.infinispan.util.concurrent.locks.LockPromise;
import org.infinispan.util.concurrent.locks.LockState;
import org.infinispan.util.concurrent.locks.impl.InfinispanLock;
import org.testng.annotations.Test;
/**
* Unit tests for the {@link InfinispanLock}.
*
* @author Pedro Ruivo
* @since 8.0
*/
@Test(groups = "unit", testName = "lock.InfinispanLockTest")
public class InfinispanLockTest extends AbstractInfinispanTest {
public void testTimeout() throws InterruptedException {
final String lockOwner1 = "LO1";
final String lockOwner2 = "LO2";
final InfinispanLock lock = new InfinispanLock(AbstractCacheTest.TIME_SERVICE);
final LockPromise lockPromise1 = lock.acquire(lockOwner1, 0, TimeUnit.MILLISECONDS);
final LockPromise lockPromise2 = lock.acquire(lockOwner2, 0, TimeUnit.MILLISECONDS);
assertTrue(lockPromise1.isAvailable());
assertTrue(lockPromise2.isAvailable());
lockPromise1.lock();
assertEquals(lockOwner1, lock.getLockOwner());
try {
lockPromise2.lock();
fail();
} catch (TimeoutException e) {
//expected!
}
lock.release(lockOwner1);
assertNull(lock.getLockOwner());
assertFalse(lock.isLocked());
//no side effects
lock.release(lockOwner2);
assertFalse(lock.isLocked());
assertNull(lock.getLockOwner());
}
public void testTimeout2() throws InterruptedException {
final String lockOwner1 = "LO1";
final String lockOwner2 = "LO2";
final String lockOwner3 = "LO3";
final InfinispanLock lock = new InfinispanLock(AbstractCacheTest.TIME_SERVICE);
final LockPromise lockPromise1 = lock.acquire(lockOwner1, 0, TimeUnit.MILLISECONDS);
final LockPromise lockPromise2 = lock.acquire(lockOwner2, 0, TimeUnit.MILLISECONDS);
final LockPromise lockPromise3 = lock.acquire(lockOwner3, 1, TimeUnit.DAYS);
assertTrue(lockPromise1.isAvailable());
assertTrue(lockPromise2.isAvailable());
assertFalse(lockPromise3.isAvailable());
lockPromise1.lock();
assertEquals(lockOwner1, lock.getLockOwner());
try {
lockPromise2.lock();
fail();
} catch (TimeoutException e) {
//expected!
}
lock.release(lockOwner1);
assertTrue(lock.isLocked());
assertTrue(lockPromise3.isAvailable());
lockPromise3.lock();
assertEquals(lockOwner3, lock.getLockOwner());
lock.release(lockOwner3);
assertFalse(lock.isLocked());
//no side effects
lock.release(lockOwner2);
assertFalse(lock.isLocked());
assertNull(lock.getLockOwner());
}
public void testTimeout3() throws InterruptedException {
final String lockOwner1 = "LO1";
final String lockOwner2 = "LO2";
final String lockOwner3 = "LO3";
final InfinispanLock lock = new InfinispanLock(AbstractCacheTest.TIME_SERVICE);
final LockPromise lockPromise1 = lock.acquire(lockOwner1, 0, TimeUnit.MILLISECONDS);
final LockPromise lockPromise2 = lock.acquire(lockOwner2, 1, TimeUnit.DAYS);
final LockPromise lockPromise3 = lock.acquire(lockOwner3, 1, TimeUnit.DAYS);
assertTrue(lockPromise1.isAvailable());
assertFalse(lockPromise2.isAvailable());
assertFalse(lockPromise3.isAvailable());
lockPromise1.lock();
assertEquals(lockOwner1, lock.getLockOwner());
//premature release. the lock is never acquired by owner 2
//when the owner 1 releases, owner 3 is able to acquire it
lock.release(lockOwner2);
assertTrue(lock.isLocked());
assertEquals(lockOwner1, lock.getLockOwner());
lock.release(lockOwner1);
assertTrue(lock.isLocked());
assertTrue(lockPromise3.isAvailable());
lockPromise3.lock();
assertEquals(lockOwner3, lock.getLockOwner());
lock.release(lockOwner3);
assertFalse(lock.isLocked());
//no side effects
lock.release(lockOwner2);
assertFalse(lock.isLocked());
assertNull(lock.getLockOwner());
}
public void testCancel() throws InterruptedException {
final InfinispanLock lock = new InfinispanLock(AbstractCacheTest.TIME_SERVICE);
final String lockOwner1 = "LO1";
final String lockOwner2 = "LO2";
final String lockOwner3 = "LO3";
ExtendedLockPromise lockPromise1 = lock.acquire(lockOwner1, 0, TimeUnit.MILLISECONDS); //will be acquired
ExtendedLockPromise lockPromise2 = lock.acquire(lockOwner2, 0, TimeUnit.MILLISECONDS); //will be timed-out
ExtendedLockPromise lockPromise3 = lock.acquire(lockOwner3, 1, TimeUnit.DAYS); //will be waiting
assertTrue(lockPromise1.isAvailable());
assertTrue(lockPromise2.isAvailable());
assertFalse(lockPromise3.isAvailable());
assertEquals(lockOwner1, lock.getLockOwner());
lockPromise1.cancel(LockState.TIMED_OUT); //no-op
lockPromise1.lock();
try {
lockPromise2.lock();
fail("TimeoutException expected");
} catch (TimeoutException e) {
//expected
}
assertEquals(lockOwner1, lock.getLockOwner());
assertTrue(lockPromise2.isAvailable());
assertFalse(lockPromise3.isAvailable());
lockPromise2.cancel(LockState.DEADLOCKED);
try {
//check state didn't change
lockPromise2.lock();
fail("TimeoutException expected");
} catch (TimeoutException e) {
//expected
}
assertEquals(lockOwner1, lock.getLockOwner());
assertFalse(lockPromise3.isAvailable());
lockPromise3.cancel(LockState.DEADLOCKED);
try {
lockPromise3.lock();
fail("DeadlockDetectedException expected");
} catch (DeadlockDetectedException e) {
//expected
}
lock.release(lockOwner1);
assertNull(lock.getLockOwner());
assertFalse(lock.isLocked());
lockPromise1 = lock.acquire(lockOwner1, 0, TimeUnit.MILLISECONDS);
lockPromise2 = lock.acquire(lockOwner2, 1, TimeUnit.DAYS);
lockPromise1.lock();
lockPromise1.cancel(LockState.TIMED_OUT);
lockPromise1.lock(); //should not throw anything
//lock2 is in WAITING state
lockPromise2.cancel(LockState.TIMED_OUT);
assertTrue(lockPromise2.isAvailable());
lock.release(lockOwner1);
try {
lockPromise2.lock();
fail("TimeoutException expected");
} catch (TimeoutException e) {
//expected
}
assertNull(lock.getLockOwner());
assertFalse(lock.isLocked());
}
public void testSingleCounter() throws ExecutionException, InterruptedException {
final NotThreadSafeCounter counter = new NotThreadSafeCounter();
final InfinispanLock counterLock = new InfinispanLock(AbstractCacheTest.TIME_SERVICE);
final int numThreads = 8;
final int maxCounterValue = 100;
final CyclicBarrier barrier = new CyclicBarrier(numThreads);
List<Future<Collection<Integer>>> callableResults = new ArrayList<>(numThreads);
for (int i = 0; i < numThreads; ++i) {
callableResults.add(fork(() -> {
final Thread lockOwner = Thread.currentThread();
assertEquals(0, counter.getCount());
List<Integer> seenValues = new LinkedList<>();
barrier.await();
while (true) {
counterLock.acquire(lockOwner, 1, TimeUnit.DAYS).lock();
assertEquals(lockOwner, counterLock.getLockOwner());
try {
int value = counter.getCount();
if (value == maxCounterValue) {
return seenValues;
}
seenValues.add(value);
counter.setCount(value + 1);
} finally {
counterLock.release(lockOwner);
}
}
}));
}
Set<Integer> seenResults = new HashSet<>();
for (Future<Collection<Integer>> future : callableResults) {
for (Integer integer : future.get()) {
assertTrue(seenResults.add(integer));
}
}
assertEquals(maxCounterValue, seenResults.size());
for (int i = 0; i < maxCounterValue; ++i) {
assertTrue(seenResults.contains(i));
}
assertFalse(counterLock.isLocked());
}
private static class NotThreadSafeCounter {
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
}