Skip to content

Commit

Permalink
Avoid creating an entry whose key is null at construction time.
Browse files Browse the repository at this point in the history
In the case of an entry whose key is stored in a `WeakReference`, the resulting entry will never be enqueued in the `ReferenceQueue`, so I'm not sure that it will necessarily ever be removed from the cache.

Compare a similar change to `MapMakerInternalMap` that was part of cl/479157599.

I get the impression that there may be a similar situation with weak _values_, but I'm not biting that off right now.

As always, we [recommend](https://guava.dev/CacheBuilder) that you use [Caffeine](https://github.com/ben-manes/caffeine/wiki) instead of Guava's `cache` library.

RELNOTES=n/a
PiperOrigin-RevId: 479318112
  • Loading branch information
cpovirk authored and Google Java Core Libraries committed Oct 6, 2022
1 parent a2e8f3c commit cb9bf09
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 36 deletions.
59 changes: 41 additions & 18 deletions android/guava/src/com/google/common/cache/LocalCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
Expand Down Expand Up @@ -456,8 +455,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
return newEntry;
}
Expand All @@ -471,8 +473,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyWriteEntry(original, newEntry);
return newEntry;
}
Expand All @@ -486,8 +491,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
copyWriteEntry(original, newEntry);
return newEntry;
Expand All @@ -509,8 +517,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
return newEntry;
}
Expand All @@ -524,8 +535,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyWriteEntry(original, newEntry);
return newEntry;
}
Expand All @@ -539,8 +553,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
copyWriteEntry(original, newEntry);
return newEntry;
Expand Down Expand Up @@ -588,13 +605,18 @@ abstract <K, V> ReferenceEntry<K, V> newEntry(
/**
* Copies an entry, assigning it a new {@code next} entry.
*
* @param original the entry to copy
* @param original the entry to copy. But avoid calling {@code getKey} on it: Instead, use the
* {@code key} parameter. That way, we prevent the key from being garbage collected in the
* case of weak keys. If we create a new entry with a key that is null at construction time,
* we're not sure if entry will necessarily ever be garbage collected.
* @param newNext entry in the same bucket
* @param key the key to copy from the original entry to the new one. Use this in preference to
* {@code original.getKey()}.
*/
// Guarded By Segment.this
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
return newEntry(segment, original.getKey(), original.getHash(), newNext);
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext, K key) {
return newEntry(segment, key, original.getHash(), newNext);
}

// Guarded By Segment.this
Expand Down Expand Up @@ -1983,7 +2005,8 @@ ReferenceEntry<K, V> newEntry(K key, int hash, @CheckForNull ReferenceEntry<K, V
*/
@GuardedBy("this")
ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
if (original.getKey() == null) {
K key = original.getKey();
if (key == null) {
// key collected
return null;
}
Expand All @@ -1995,7 +2018,7 @@ ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K,
return null;
}

ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext);
ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext, key);
newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry));
return newEntry;
}
Expand Down
59 changes: 41 additions & 18 deletions guava/src/com/google/common/cache/LocalCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
Expand Down Expand Up @@ -459,8 +458,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
return newEntry;
}
Expand All @@ -474,8 +476,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyWriteEntry(original, newEntry);
return newEntry;
}
Expand All @@ -489,8 +494,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
copyWriteEntry(original, newEntry);
return newEntry;
Expand All @@ -512,8 +520,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
return newEntry;
}
Expand All @@ -527,8 +538,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyWriteEntry(original, newEntry);
return newEntry;
}
Expand All @@ -542,8 +556,11 @@ <K, V> ReferenceEntry<K, V> newEntry(

@Override
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext);
Segment<K, V> segment,
ReferenceEntry<K, V> original,
ReferenceEntry<K, V> newNext,
K key) {
ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext, key);
copyAccessEntry(original, newEntry);
copyWriteEntry(original, newEntry);
return newEntry;
Expand Down Expand Up @@ -591,13 +608,18 @@ abstract <K, V> ReferenceEntry<K, V> newEntry(
/**
* Copies an entry, assigning it a new {@code next} entry.
*
* @param original the entry to copy
* @param original the entry to copy. But avoid calling {@code getKey} on it: Instead, use the
* {@code key} parameter. That way, we prevent the key from being garbage collected in the
* case of weak keys. If we create a new entry with a key that is null at construction time,
* we're not sure if entry will necessarily ever be garbage collected.
* @param newNext entry in the same bucket
* @param key the key to copy from the original entry to the new one. Use this in preference to
* {@code original.getKey()}.
*/
// Guarded By Segment.this
<K, V> ReferenceEntry<K, V> copyEntry(
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
return newEntry(segment, original.getKey(), original.getHash(), newNext);
Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext, K key) {
return newEntry(segment, key, original.getHash(), newNext);
}

// Guarded By Segment.this
Expand Down Expand Up @@ -1983,7 +2005,8 @@ ReferenceEntry<K, V> newEntry(K key, int hash, @Nullable ReferenceEntry<K, V> ne
*/
@GuardedBy("this")
ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) {
if (original.getKey() == null) {
K key = original.getKey();
if (key == null) {
// key collected
return null;
}
Expand All @@ -1995,7 +2018,7 @@ ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K,
return null;
}

ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext);
ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext, key);
newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry));
return newEntry;
}
Expand Down

0 comments on commit cb9bf09

Please sign in to comment.