diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java index 26e44e7..c55ecbe 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java @@ -74,6 +74,7 @@ public class CacheLIRS implements LoadingCache { private static final Logger LOG = LoggerFactory.getLogger(CacheLIRS.class); + private static final ThreadLocal CURRENTLY_LOADING = new ThreadLocal(); /** * The maximum memory this cache should use. */ @@ -859,6 +860,15 @@ public class CacheLIRS implements LoadingCache { if (value != null) { return value; } + // if we are within a loader, and are currently loading + // an entry, then we need to avoid a possible deadlock + // (we ensure that while loading an entry, we only load + // entries with a higher hash code, so there is a clear order) + Integer outer = CURRENTLY_LOADING.get(); + if (outer != null && hash <= outer) { + // to prevent a deadlock, we also load the value ourselves + return load(key, hash, valueLoader); + } ConcurrentHashMap loading = cache.loadingInProgress; // the object we have to wait for in case another thread loads // this value @@ -874,11 +884,13 @@ public class CacheLIRS implements LoadingCache { if (alreadyLoading == null) { // we are loading ourselves try { + CURRENTLY_LOADING.set(hash); return load(key, hash, valueLoader); } finally { loading.remove(key); // notify other threads loadNow.notifyAll(); + CURRENTLY_LOADING.remove(); } } } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java index 0600652..9547a18 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/cache/ConcurrentTest.java @@ -107,7 +107,7 @@ public class ConcurrentTest { final CacheLIRS cache = new CacheLIRS.Builder(). maximumWeight(100).averageWeight(10).build(); final Exception[] ex = new Exception[1]; - final int entryCount = 100; + final int entryCount = 10; int size = 3; Thread[] threads = new Thread[size]; final AtomicBoolean stop = new AtomicBoolean(); @@ -118,7 +118,11 @@ public class ConcurrentTest { Callable callable = new Callable() { @Override public Integer call() throws ExecutionException { - cache.get(r.nextInt(entryCount)); + if (r.nextBoolean()) { + cache.get(r.nextInt(entryCount), this); + } else { + cache.get(r.nextInt(entryCount)); + } return 1; } };