aboutsummaryrefslogtreecommitdiff
path: root/android/guava/src/com/google/common/cache/LocalCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/guava/src/com/google/common/cache/LocalCache.java')
-rw-r--r--android/guava/src/com/google/common/cache/LocalCache.java39
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