You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2014/02/04 15:41:58 UTC
svn commit: r1564322 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java
test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java
Author: thomasm
Date: Tue Feb 4 14:41:58 2014
New Revision: 1564322
URL: http://svn.apache.org/r1564322
Log:
OAK-1364 CacheLIRS concurrency issue (allow concurrent clear and refresh without loader)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java?rev=1564322&r1=1564321&r2=1564322&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java Tue Feb 4 14:41:58 2014
@@ -619,7 +619,9 @@ public class CacheLIRS<K, V> implements
int queue2Size;
/**
- * The map array. The size is always a power of 2.
+ * The map array. The size is always a power of 2. The bit mask that is
+ * applied to the key hash code to get the index in the map array. The
+ * mask is the length of the array minus one.
*/
Entry<K, V>[] entries;
@@ -657,12 +659,6 @@ public class CacheLIRS<K, V> implements
private int averageMemory;
/**
- * The bit mask that is applied to the key hash code to get the index in the
- * map array. The mask is the length of the array minus one.
- */
- private int mask;
-
- /**
* The LIRS stack size.
*/
private int stackSize;
@@ -722,8 +718,6 @@ public class CacheLIRS<K, V> implements
}
// the array size is at most 2^31 elements
int len = (int) Math.min(1L << 31, l);
- // the bit mask has all bits set
- mask = len - 1;
// initialize the stack and queue heads
stack = new Entry<K, V>();
@@ -733,8 +727,10 @@ public class CacheLIRS<K, V> implements
queue2 = new Entry<K, V>();
queue2.queuePrev = queue2.queueNext = queue2;
- // first set to null - avoiding out of memory
- entries = null;
+ // first set to a small array, to avoiding out of memory
+ @SuppressWarnings("unchecked")
+ Entry<K, V>[] small = new Entry[1];
+ entries = small;
@SuppressWarnings("unchecked")
Entry<K, V>[] e = new Entry[len];
entries = e;
@@ -920,6 +916,10 @@ public class CacheLIRS<K, V> implements
}
synchronized void refresh(K key, int hash, CacheLoader<K, V> loader) throws ExecutionException {
+ if (loader == null) {
+ // no loader - no refresh
+ return;
+ }
V value;
V old = get(key, hash);
long start = System.nanoTime();
@@ -968,9 +968,11 @@ public class CacheLIRS<K, V> implements
e.key = key;
e.value = value;
e.memory = memory;
+ Entry<K, V>[] array = entries;
+ int mask = array.length - 1;
int index = hash & mask;
- e.mapNext = entries[index];
- entries[index] = e;
+ e.mapNext = array[index];
+ array[index] = e;
usedMemory += memory;
if (usedMemory > maxMemory && mapSize > 0) {
// an old entry needs to be removed
@@ -990,13 +992,15 @@ public class CacheLIRS<K, V> implements
* @param hash the hash
*/
synchronized void invalidate(Object key, int hash) {
+ Entry<K, V>[] array = entries;
+ int mask = array.length - 1;
int index = hash & mask;
- Entry<K, V> e = entries[index];
+ Entry<K, V> e = array[index];
if (e == null) {
return;
}
if (e.key.equals(key)) {
- entries[index] = e.mapNext;
+ array[index] = e.mapNext;
} else {
Entry<K, V> last;
do {
@@ -1107,8 +1111,10 @@ public class CacheLIRS<K, V> implements
* @return the entry (might be a non-resident)
*/
Entry<K, V> find(Object key, int hash) {
+ Entry<K, V>[] array = entries;
+ int mask = array.length - 1;
int index = hash & mask;
- Entry<K, V> e = entries[index];
+ Entry<K, V> e = array[index];
while (e != null && !e.key.equals(key)) {
e = e.mapNext;
}
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java?rev=1564322&r1=1564321&r2=1564322&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java Tue Feb 4 14:41:58 2014
@@ -20,6 +20,7 @@ package org.apache.jackrabbit.oak.cache;
import java.util.Random;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
@@ -32,57 +33,60 @@ public class ConcurrentTest {
public void testRandomOperations() throws Exception {
Random r = new Random(1);
final CacheLIRS<Integer, Integer> cache = new CacheLIRS.Builder().
- maximumWeight(100).build();
+ maximumWeight(100).averageWeight(10).build();
final Exception[] ex = new Exception[1];
int size = 3;
Thread[] threads = new Thread[size];
+ final AtomicBoolean stop = new AtomicBoolean();
for (int i = 0; i < size; i++) {
Thread t = new Thread() {
@Override
public void run() {
- try {
- cache.cleanUp();
- cache.containsKey(1);
- cache.containsValue(1);
- cache.entrySet();
- cache.getMaxMemory();
- cache.getIfPresent(1);
- cache.getAverageMemory();
- cache.getMemory(1);
- cache.getUsedMemory();
- cache.invalidate(1);
- cache.invalidateAll();
- cache.isEmpty();
- cache.keySet();
- cache.peek(1);
- cache.put(1, 10);
- cache.refresh(1);
- cache.remove(1);
- cache.setAverageMemory(10);
- cache.setMaxMemory(10);
- cache.size();
- cache.stats();
- ConcurrentMap<Integer, Integer> map = cache.asMap();
- map.size();
- map.isEmpty();
- map.containsKey(1);
- map.containsValue(1);
- map.get(1);
- map.put(1, 10);
- map.remove(1);
- map.clear();
- map.keySet();
- map.values();
- map.entrySet();
- map.putIfAbsent(1, 10);
- map.remove(1);
- map.remove(1, 10);
- map.replace(1, 10, 100);
- map.replace(1, 10);
- cache.get(1);
- cache.getUnchecked(1);
- } catch (Exception e) {
- ex[0] = e;
+ while (!stop.get()) {
+ try {
+ cache.cleanUp();
+ cache.containsKey(1);
+ cache.containsValue(2);
+ cache.entrySet();
+ cache.getMaxMemory();
+ cache.getIfPresent(3);
+ cache.getAverageMemory();
+ cache.getMemory(4);
+ cache.getUsedMemory();
+ cache.invalidate(5);
+ cache.invalidateAll();
+ cache.isEmpty();
+ cache.keySet();
+ cache.peek(6);
+ cache.put(7, 8);
+ cache.refresh(9);
+ cache.remove(10);
+ cache.setAverageMemory(11);
+ cache.setMaxMemory(12);
+ cache.size();
+ cache.stats();
+ ConcurrentMap<Integer, Integer> map = cache.asMap();
+ map.size();
+ map.isEmpty();
+ map.containsKey(1);
+ map.containsValue(1);
+ map.get(11);
+ map.put(12, 10);
+ map.remove(13);
+ map.clear();
+ map.keySet();
+ map.values();
+ map.entrySet();
+ map.putIfAbsent(14, 10);
+ map.remove(15);
+ map.remove(16, 10);
+ map.replace(17, 10, 100);
+ map.replace(18, 10);
+ cache.get(19);
+ cache.getUnchecked(1);
+ } catch (Exception e) {
+ ex[0] = e;
+ }
}
}
};
@@ -91,12 +95,13 @@ public class ConcurrentTest {
}
try {
long start = System.currentTimeMillis();
- while (System.currentTimeMillis() < start + 100) {
+ while (System.currentTimeMillis() < start + 1000) {
for (int i = 0; i < 100000 && ex[0] == null; i++) {
- cache.put(r.nextInt(1000), r.nextInt(10000));
+ cache.put(r.nextInt(10), r.nextInt(10000));
}
}
} finally {
+ stop.set(true);
for (Thread t : threads) {
t.join();
}