diff options
Diffstat (limited to 'android/guava/src/com/google/common/cache/LocalCache.java')
-rw-r--r-- | android/guava/src/com/google/common/cache/LocalCache.java | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/android/guava/src/com/google/common/cache/LocalCache.java b/android/guava/src/com/google/common/cache/LocalCache.java index 239afe204..6fb3945df 100644 --- a/android/guava/src/com/google/common/cache/LocalCache.java +++ b/android/guava/src/com/google/common/cache/LocalCache.java @@ -277,7 +277,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> // will result in random eviction behavior. int segmentShift = 0; int segmentCount = 1; - while (segmentCount < concurrencyLevel && (!evictsBySize() || segmentCount * 20 <= maxWeight)) { + while (segmentCount < concurrencyLevel + && (!evictsBySize() || segmentCount * 20L <= maxWeight)) { ++segmentShift; segmentCount <<= 1; } @@ -2179,12 +2180,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> if (createNewEntry) { try { - // Synchronizes on the entry to allow failing fast when a recursive load is - // detected. This may be circumvented when an entry is copied, but will fail fast most - // of the time. - synchronized (e) { - return loadSync(key, hash, loadingValueReference, loader); - } + return loadSync(key, hash, loadingValueReference, loader); } finally { statsCounter.recordMisses(1); } @@ -2200,7 +2196,22 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> throw new AssertionError(); } - checkState(!Thread.holdsLock(e), "Recursive load of: %s", key); + // As of this writing, the only prod ValueReference implementation for which isLoading() is + // true is LoadingValueReference. (Note, however, that not all LoadingValueReference instances + // have isLoading()==true: LoadingValueReference has a subclass, ComputingValueReference, for + // which isLoading() is false!) However, that might change, and we already have a *test* + // implementation for which it doesn't hold. So we check instanceof to be safe. + if (valueReference instanceof LoadingValueReference) { + // We check whether the thread that is loading the entry is our current thread, which would + // mean that we are both loading and waiting for the entry. In this case, we fail fast + // instead of deadlocking. + checkState( + ((LoadingValueReference<K, V>) valueReference).getLoadingThread() + != Thread.currentThread(), + "Recursive load of: %s", + key); + } + // don't consider expiration as we're concurrent with loading try { V value = valueReference.waitForValue(); @@ -3426,12 +3437,20 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> final SettableFuture<V> futureValue = SettableFuture.create(); final Stopwatch stopwatch = Stopwatch.createUnstarted(); + final Thread loadingThread; + public LoadingValueReference() { this(LocalCache.<K, V>unset()); } + /* + * TODO(cpovirk): Consider making this implementation closer to the mainline implementation. + * (The difference was introduced as part of Java-8-specific changes in cl/132882204, but we + * could probably make *some* of those changes here in the backport, too.) + */ public LoadingValueReference(ValueReference<K, V> oldValue) { this.oldValue = oldValue; + this.loadingThread = Thread.currentThread(); } @Override @@ -3535,6 +3554,10 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> ReferenceQueue<V> queue, @CheckForNull V value, ReferenceEntry<K, V> entry) { return this; } + + Thread getLoadingThread() { + return this.loadingThread; + } } // Queues |