Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions guava-tests/test/com/google/common/cache/LocalCacheTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,44 @@ public void testRemovalListener_collected() {
assertThat(listener.isEmpty()).isTrue();
}

// Test for https://github.com/google/guava/issues/7985
// When compute() returns null for a collected entry, the removal cause should be COLLECTED.
public void testComputeRemovalCause_collected() {
QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
LocalCache<Object, Object> map =
makeLocalCache(
createCacheBuilder().concurrencyLevel(1).softValues().removalListener(listener));
Segment<Object, Object> segment = map.segments[0];
assertThat(listener.isEmpty()).isTrue();

Object key = new Object();
Object value = new Object();

// Put an entry into the cache
map.put(key, value);
assertThat(listener.isEmpty()).isTrue();

// Get the entry and clear its value reference to simulate garbage collection
int hash = map.hash(key);
ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash);
ValueReference<Object, Object> valueReference = entry.getValueReference();

// Simulate garbage collection by clearing the value reference
((java.lang.ref.Reference<?>) valueReference).clear();

// Now call compute() with a function that returns null
// Since the value was collected, the removal cause should be COLLECTED, not EXPLICIT
Object unused = map.compute(key, (k, v) -> {
// The value should be null since it was collected
assertThat(v).isNull();
return null;
});

// Verify the removal notification has COLLECTED cause
assertNotified(listener, key, null, RemovalCause.COLLECTED);
assertThat(listener.isEmpty()).isTrue();
}

public void testRemovalListener_expired() {
FakeTicker ticker = new FakeTicker();
QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
Expand Down
8 changes: 7 additions & 1 deletion guava/src/com/google/common/cache/LocalCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -2287,7 +2287,13 @@ V waitForLoadingValue(ReferenceEntry<K, V> e, K key, ValueReference<K, V> valueR
removeLoadingValue(key, hash, computingValueReference);
return null;
} else {
removeEntry(e, hash, RemovalCause.EXPLICIT);
// If the value was garbage collected, report COLLECTED; otherwise EXPLICIT.
V oldValue = valueReference.get();
RemovalCause cause =
(oldValue == null && valueReference.isActive())
? RemovalCause.COLLECTED
: RemovalCause.EXPLICIT;
removeEntry(e, hash, cause);
return null;
}
} finally {
Expand Down
Loading