package com.orientechnologies.orient.core.index.hashindex.local.cache;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import com.orientechnologies.orient.core.storage.cache.local.twoq.ConcurrentLRUList;
import com.orientechnologies.orient.core.storage.cache.local.twoq.LRUList;
import com.orientechnologies.orient.core.storage.cache.OCacheEntry;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.orientechnologies.orient.test.ConcurrentTestHelper;
import com.orientechnologies.orient.test.TestFactory;
/**
* Concurrent test for {@link ConcurrentLRUList}.
*
* @author Artem Orobets (enisher-at-gmail.com)
*/
public class ConcurrentLRUListConcurrentTest {
private static final int AMOUNT_OF_OPERATIONS = 100000;
private static final int THREAD_COUNT = 8;
private LRUList list = new ConcurrentLRUList();
private volatile long c = 47;
@BeforeMethod
public void setUp() throws Exception {
list = new ConcurrentLRUList();
}
@Test
public void testConcurrentAdd() throws Exception {
ConcurrentTestHelper.test(THREAD_COUNT, new AdderFactory());
int expectedSize = AMOUNT_OF_OPERATIONS * THREAD_COUNT;
assertListConsistency(expectedSize);
}
@Test
public void testConcurrentAddAndRemove() throws Exception {
Collection<Integer> res = ConcurrentTestHelper.<Integer> build().add(THREAD_COUNT, new AdderFactory())
.add(THREAD_COUNT, new RemoveLRUFactory()).go();
int expectedSize = 0;
for (Integer r : res) {
expectedSize += r;
}
assertListConsistency(expectedSize);
}
@Test
public void testAddRemoveSameEntries() throws Exception {
ConcurrentTestHelper.<Integer> build().add(THREAD_COUNT, new AddSameFactory()).add(THREAD_COUNT, new RemoveLRUFactory()).go();
assertListConsistency();
}
@Test
public void testAllOperationsRandomEntries() throws Exception {
ConcurrentTestHelper.<Integer> build().add(THREAD_COUNT, new RandomAdderFactory()).add(THREAD_COUNT, new RandomRemoveFactory())
.add(THREAD_COUNT, new RemoveLRUFactory()).go();
assertListConsistency();
}
private void assertListConsistency(int expectedSize) {
Assert.assertEquals(list.size(), expectedSize);
int count = 0;
List<OCacheEntry> items = new ArrayList<OCacheEntry>();
for (OCacheEntry entry : list) {
items.add(entry);
count++;
}
Assert.assertEquals(count, expectedSize);
Collections.reverse(items);
for (OCacheEntry item : items) {
OCacheEntry actual = list.removeLRU();
Assert.assertEquals(actual, item);
}
Assert.assertNull(list.removeLRU());
}
private void assertListConsistency() {
int expectedSize = list.size();
int count = 0;
List<OCacheEntry> items = new ArrayList<OCacheEntry>();
for (OCacheEntry entry : list) {
items.add(entry);
count++;
}
Assert.assertEquals(count, expectedSize);
Collections.reverse(items);
for (OCacheEntry item : items) {
OCacheEntry actual = list.removeLRU();
Assert.assertEquals(actual, item);
}
Assert.assertNull(list.removeLRU());
}
private void consumeCPU(int cycles) {
long c1 = c;
for (int i = 0; i < cycles; i++) {
c1 += c1 * 31 + i * 51;
}
c = c1;
}
private class AdderFactory implements TestFactory<Integer> {
private int j = 0;
@Override
public Callable<Integer> createWorker() {
return new Callable<Integer>() {
private int threadNumber = ++j;
@Override
public Integer call() throws Exception {
for (int i = 0; i < AMOUNT_OF_OPERATIONS; i++) {
list.putToMRU(new OCacheEntry(threadNumber, i, null, false));
}
return AMOUNT_OF_OPERATIONS;
}
};
}
}
private class RemoveLRUFactory implements TestFactory<Integer> {
@Override
public Callable<Integer> createWorker() {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int actualRemoves = 0;
consumeCPU(1000);
for (int i = 0; i < AMOUNT_OF_OPERATIONS; i++) {
OCacheEntry e = list.removeLRU();
if (e != null) {
actualRemoves++;
}
consumeCPU(1000);
}
return -actualRemoves;
}
};
}
}
private class RandomAdderFactory implements TestFactory<Integer> {
@Override
public Callable<Integer> createWorker() {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Random r = new Random();
for (int i = 0; i < AMOUNT_OF_OPERATIONS; i++) {
list.putToMRU(new OCacheEntry(0, r.nextInt(200), null, false));
consumeCPU(r.nextInt(500) + 1000);
}
return AMOUNT_OF_OPERATIONS;
}
};
}
}
private class AddSameFactory implements TestFactory<Integer> {
@Override
public Callable<Integer> createWorker() {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Random r = new Random();
for (int i = 0; i < AMOUNT_OF_OPERATIONS; i++) {
list.putToMRU(new OCacheEntry(0, 0, null, false));
consumeCPU(r.nextInt(500) + 1000);
}
return AMOUNT_OF_OPERATIONS;
}
};
}
}
private class RandomRemoveFactory implements TestFactory<Integer> {
@Override
public Callable<Integer> createWorker() {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Random r = new Random();
int actualRemoves = 0;
for (int i = 0; i < AMOUNT_OF_OPERATIONS; i++) {
OCacheEntry e = list.remove(0, r.nextInt(100));
if (e != null) {
actualRemoves++;
}
consumeCPU(r.nextInt(1000) + 1000);
}
return -actualRemoves;
}
};
}
}
}