package org.infinispan.lock; 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.LockPromise; import org.infinispan.util.concurrent.locks.impl.LockContainer; import org.infinispan.util.concurrent.locks.impl.PerKeyLockContainer; import org.infinispan.util.concurrent.locks.impl.StripedLockContainer; import org.testng.AssertJUnit; import org.testng.annotations.Test; /** * Unit test for {@link LockContainer}. * * @author Pedro Ruivo * @since 8.0 */ @Test(groups = "unit", testName = "lock.LockContainerTest") public class LockContainerTest extends AbstractInfinispanTest { public void testSingleLockWithPerEntry() throws InterruptedException { PerKeyLockContainer lockContainer = new PerKeyLockContainer(); lockContainer.inject(AbstractCacheTest.TIME_SERVICE); doSingleLockTest(lockContainer, -1); } public void testSingleCounterTestPerEntry() throws ExecutionException, InterruptedException { PerKeyLockContainer lockContainer = new PerKeyLockContainer(); lockContainer.inject(AbstractCacheTest.TIME_SERVICE); doSingleCounterTest(lockContainer, -1); } public void testSingleLockWithStriped() throws InterruptedException { StripedLockContainer lockContainer = new StripedLockContainer(16); lockContainer.inject(AbstractCacheTest.TIME_SERVICE); doSingleLockTest(lockContainer, 16); } public void testSingleCounterWithStriped() throws ExecutionException, InterruptedException { StripedLockContainer lockContainer = new StripedLockContainer(16); lockContainer.inject(AbstractCacheTest.TIME_SERVICE); doSingleCounterTest(lockContainer, 16); } private void doSingleCounterTest(LockContainer lockContainer, int poolSize) throws InterruptedException, ExecutionException { final NotThreadSafeCounter counter = new NotThreadSafeCounter(); final String key = "key"; 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(); AssertJUnit.assertEquals(0, counter.getCount()); List<Integer> seenValues = new LinkedList<>(); barrier.await(); while (true) { lockContainer.acquire(key, lockOwner, 1, TimeUnit.DAYS).lock(); AssertJUnit.assertEquals(lockOwner, lockContainer.getLock(key).getLockOwner()); try { int value = counter.getCount(); if (value == maxCounterValue) { return seenValues; } seenValues.add(value); counter.setCount(value + 1); } finally { lockContainer.release(key, lockOwner); } } })); } Set<Integer> seenResults = new HashSet<>(); for (Future<Collection<Integer>> future : callableResults) { for (Integer integer : future.get()) { AssertJUnit.assertTrue(seenResults.add(integer)); } } AssertJUnit.assertEquals(maxCounterValue, seenResults.size()); for (int i = 0; i < maxCounterValue; ++i) { AssertJUnit.assertTrue(seenResults.contains(i)); } AssertJUnit.assertEquals(0, lockContainer.getNumLocksHeld()); if (poolSize == -1) { AssertJUnit.assertEquals(0, lockContainer.size()); } else { AssertJUnit.assertEquals(poolSize, lockContainer.size()); } } private void doSingleLockTest(LockContainer container, int poolSize) throws InterruptedException { final String lockOwner1 = "LO1"; final String lockOwner2 = "LO2"; final String lockOwner3 = "LO3"; final LockPromise lockPromise1 = container.acquire("key", lockOwner1, 0, TimeUnit.MILLISECONDS); final LockPromise lockPromise2 = container.acquire("key", lockOwner2, 0, TimeUnit.MILLISECONDS); final LockPromise lockPromise3 = container.acquire("key", lockOwner3, 0, TimeUnit.MILLISECONDS); AssertJUnit.assertEquals(1, container.getNumLocksHeld()); if (poolSize == -1) { //dynamic AssertJUnit.assertEquals(1, container.size()); } else { AssertJUnit.assertEquals(poolSize, container.size()); } acquireLock(lockPromise1, false); acquireLock(lockPromise2, true); acquireLock(lockPromise3, true); AssertJUnit.assertEquals(1, container.getNumLocksHeld()); if (poolSize == -1) { //dynamic AssertJUnit.assertEquals(1, container.size()); } else { AssertJUnit.assertEquals(poolSize, container.size()); } container.release("key", lockOwner2); container.release("key", lockOwner3); AssertJUnit.assertEquals(1, container.getNumLocksHeld()); if (poolSize == -1) { //dynamic AssertJUnit.assertEquals(1, container.size()); } else { AssertJUnit.assertEquals(poolSize, container.size()); } container.release("key", lockOwner1); AssertJUnit.assertEquals(0, container.getNumLocksHeld()); if (poolSize == -1) { //dynamic AssertJUnit.assertEquals(0, container.size()); } else { AssertJUnit.assertEquals(poolSize, container.size()); } } private void acquireLock(LockPromise promise, boolean timeout) throws InterruptedException { try { promise.lock(); AssertJUnit.assertFalse(timeout); } catch (TimeoutException e) { AssertJUnit.assertTrue(timeout); } } private static class NotThreadSafeCounter { private int count; public int getCount() { return count; } public void setCount(int count) { this.count = count; } } }