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();
             }