diff --git a/caffeine/build.gradle.kts b/caffeine/build.gradle.kts index b64c0a1ce2..84c2afdb22 100644 --- a/caffeine/build.gradle.kts +++ b/caffeine/build.gradle.kts @@ -132,23 +132,32 @@ val generateNodes by tasks.registering(JavaExec::class) { tasks.named("compileJava").configure { dependsOn(generateLocalCaches, generateNodes) finalizedBy(compileCodeGenJava) - options.errorprone { - disable("CheckReturnValue") + options.apply { + compilerArgs.addAll(listOf("-Xlint:-auxiliaryclass", "-Xlint:-exports")) + errorprone { + disable("CheckReturnValue") + } } } tasks.named("compileTestJava").configure { dependsOn(tasks.jar, compileCodeGenJava) - options.errorprone { - disable("Varifier") - disable("Var") + options.apply { + compilerArgs.add("-Xlint:-auxiliaryclass") + errorprone { + disable("Varifier") + disable("Var") + } } } tasks.named("compileJmhJava").configure { - options.errorprone { - disable("Varifier") - disable("Var") + options.apply { + compilerArgs.add("-Xlint:-classfile") + errorprone { + disable("Varifier") + disable("Var") + } } } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java index 1047479153..1dfa6cff56 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java @@ -1061,8 +1061,8 @@ public void evict_zeroWeight_victim(BoundedLocalCache cache, CacheCont @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, maximumSize = Maximum.FULL) public void evict_admit(BoundedLocalCache cache, CacheContext context) { - // This test require that the JVM is run to allow the internal random fields can be overridden - // by adding the JVM argument --add-opens java.base/java.lang=ALL-UNNAMED + // This test modifies the thread's internal fields so that the random admission is predictable + // and therefore requires the JVM argument --add-opens java.base/java.lang=ALL-UNNAMED Reset.resetThreadLocalRandom(); cache.frequencySketch().ensureCapacity(context.maximumSize()); diff --git a/gradle/config/spotbugs/exclude.xml b/gradle/config/spotbugs/exclude.xml index 545e62444c..301505ca2e 100644 --- a/gradle/config/spotbugs/exclude.xml +++ b/gradle/config/spotbugs/exclude.xml @@ -522,6 +522,7 @@ diff --git a/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts index 1934546c74..8b2b3d3a3f 100644 --- a/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts @@ -39,8 +39,7 @@ tasks.withType().configureEach { options.apply { javaModuleVersion = provider { version as String } - compilerArgs.addAll(listOf("-Xlint:all", "-Xlint:-auxiliaryclass", "-Xlint:-classfile", - "-Xlint:-exports", "-Xlint:-processing", "-Xlint:-requires-automatic", "-parameters", + compilerArgs.addAll(listOf("-Xlint:all", "-parameters", "-Xmaxerrs", "500", "-Xmaxwarns", "500")) if (isCI()) { compilerArgs.add("-Werror") diff --git a/guava/build.gradle.kts b/guava/build.gradle.kts index 6b7ca5d092..d494d15d69 100644 --- a/guava/build.gradle.kts +++ b/guava/build.gradle.kts @@ -14,6 +14,10 @@ dependencies { testImplementation(libs.bundles.slf4j.nop) } +tasks.named("compileJava").configure { + options.compilerArgs.add("-Xlint:-requires-automatic") +} + tasks.named("compileTestJava").configure { options.errorprone { disable("Varifier") diff --git a/simulator/build.gradle.kts b/simulator/build.gradle.kts index e5847d57ce..9230aac62f 100644 --- a/simulator/build.gradle.kts +++ b/simulator/build.gradle.kts @@ -50,11 +50,18 @@ forbiddenApis { "jdk-internal", "jdk-non-portable", "jdk-reflection", "jdk-unsafe")) } -tasks.withType().configureEach { - options.errorprone { - disableWarningsInGeneratedCode = true - disable("SystemOut") - nullaway.disable() +tasks.named("compileJava").configure { + options.apply { + compilerArgs.addAll(listOf("-Xlint:-classfile", "-Xlint:-processing")) + errorprone { + disableWarningsInGeneratedCode = true + disable("SystemOut") + + nullaway { + externalInitAnnotations.add("picocli.CommandLine.Command") + treatGeneratedAsUnannotated = true + } + } } } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/BasicSettings.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/BasicSettings.java index 06b2ca9421..4a9a46e122 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/BasicSettings.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/BasicSettings.java @@ -100,11 +100,13 @@ public Config config() { } /** Gets the quoted integer at the given path, ignoring comma and underscore separators */ + @SuppressWarnings("NullAway") protected int getFormattedInt(String path) { return parseFormattedNumber(path, config()::getInt, Ints::tryParse); } /** Gets the quoted long at the given path, ignoring comma and underscore separators */ + @SuppressWarnings("NullAway") protected long getFormattedLong(String path) { return parseFormattedNumber(path, config()::getLong, Longs::tryParse); } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/Simulator.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/Simulator.java index d241fcce8e..14d7ec299b 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/Simulator.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/Simulator.java @@ -146,7 +146,9 @@ private static void throwError(RuntimeException error, Iterable pol try { policy.completed().join(); } catch (CompletionException e) { - Throwables.throwIfUnchecked(e.getCause()); + if (e.getCause() != null) { + Throwables.throwIfUnchecked(e.getCause()); + } e.addSuppressed(error); throw e; } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java index 13a8099a54..c945b11888 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java @@ -32,6 +32,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntPriorityQueue; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; /** @@ -47,11 +48,13 @@ public final class Clairvoyant implements KeyOnlyAdmittor { private final PolicyStats policyStats; public Clairvoyant(Config config, PolicyStats policyStats) { - if (snapshot.get() == null) { - snapshot.set(readAccessTimes(new BasicSettings(config))); + @Var var readOnlyAccessTimes = snapshot.get(); + if (readOnlyAccessTimes == null) { + readOnlyAccessTimes = readAccessTimes(new BasicSettings(config)); + snapshot.set(readOnlyAccessTimes); } - accessTimes = new Long2ObjectOpenHashMap<>(snapshot.get().size()); - for (var entry : snapshot.get().long2ObjectEntrySet()) { + accessTimes = new Long2ObjectOpenHashMap<>(readOnlyAccessTimes.size()); + for (var entry : readOnlyAccessTimes.long2ObjectEntrySet()) { var times = new IntArrayFIFOQueue(entry.getValue().size()); accessTimes.put(entry.getLongKey(), times); entry.getValue().forEach(times::enqueue); @@ -108,6 +111,6 @@ private static Long2ObjectMap readAccessTimes(BasicSettings settings) { times.add(++tick[0]); }); } - return accessTimes; + return Long2ObjectMaps.unmodifiable(accessTimes); } } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/countmin4/CountMin4.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/countmin4/CountMin4.java index 76eafcb31f..c161eb00e3 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/countmin4/CountMin4.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/countmin4/CountMin4.java @@ -46,7 +46,7 @@ public abstract class CountMin4 implements Frequency { * Creates a frequency sketch that can accurately estimate the popularity of elements given * the maximum size of the cache. */ - @SuppressWarnings({"this-escape", "Varifier"}) + @SuppressWarnings({"NullAway.Init", "this-escape", "Varifier"}) protected CountMin4(Config config) { var settings = new BasicSettings(config); conservative = settings.tinyLfu().conservative(); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/membership/bloom/BloomFilter.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/membership/bloom/BloomFilter.java index 1cff5bf352..d00ce676ae 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/membership/bloom/BloomFilter.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/membership/bloom/BloomFilter.java @@ -46,6 +46,7 @@ public final class BloomFilter implements Membership { * Creates a lazily initialized membership sketch, requiring {@link #ensureCapacity} be called * when the expected number of insertions and the false positive probability have been determined. */ + @SuppressWarnings("NullAway.Init") public BloomFilter() {} /** diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/AbstractTraceReader.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/AbstractTraceReader.java index f14fe70de2..11cc196666 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/AbstractTraceReader.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/AbstractTraceReader.java @@ -120,7 +120,7 @@ protected BufferedInputStream readInput(InputStream input) { try { var archive = new ArchiveStreamFactory().createArchiveInputStream(input); var entries = new AbstractIterator() { - @Override protected InputStream computeNext() { + @Override protected @Nullable InputStream computeNext() { try { return (archive.getNextEntry() == null) ? endOfData() diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/BinaryTraceReader.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/BinaryTraceReader.java index 248990bd40..4010ddffe5 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/BinaryTraceReader.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/BinaryTraceReader.java @@ -29,6 +29,8 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.policy.AccessEvent; import com.google.common.io.Closeables; @@ -57,7 +59,7 @@ public Stream events() { private final class TraceIterator implements Iterator { final DataInputStream input; - AccessEvent next; + @Nullable AccessEvent next; boolean ready; TraceIterator(DataInputStream input) { @@ -85,6 +87,7 @@ public AccessEvent next() { if (!hasNext()) { throw new NoSuchElementException(); } + requireNonNull(next); ready = false; return next; } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/TraceFormat.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/TraceFormat.java index 77504269da..433fe70feb 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/TraceFormat.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/parser/TraceFormat.java @@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Sets.toImmutableEnumSet; import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; import java.util.List; import java.util.function.Function; @@ -127,7 +128,7 @@ private ImmutableList readers() { return filePaths.stream().map(path -> { List parts = Splitter.on(':').limit(2).splitToList(path); TraceFormat format = (parts.size() == 1) ? TraceFormat.this : named(parts.get(0)); - return format.factory.apply(Iterables.getLast(parts)); + return format.factory.apply(requireNonNull(Iterables.getLast(parts))); }).collect(toImmutableList()); } }; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/PolicyActor.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/PolicyActor.java index 97a112136e..c9e3523f94 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/PolicyActor.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/PolicyActor.java @@ -46,6 +46,7 @@ public final class PolicyActor { * @param policy the cache policy being simulated * @param settings the simulation settings */ + @SuppressWarnings("NullAway") public PolicyActor(Thread parent, Policy policy, BasicSettings settings) { this.semaphore = new Semaphore(settings.actor().mailboxSize()); this.future = CompletableFuture.completedFuture(null); @@ -70,6 +71,7 @@ public void finish() { } /** Submits the command to the mailbox and blocks until accepted. */ + @SuppressWarnings("NullAway") private void submit(Command command) { try { semaphore.acquire(); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/ArcPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/ArcPolicy.java index 134395ba03..b242f415e9 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/ArcPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/ArcPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.adaptive; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -161,21 +162,21 @@ private void onMiss(long key) { int sizeL2 = (sizeT2 + sizeB2); if (sizeL1 == maximumSize) { if (sizeT1 < maximumSize) { - Node victim = headB1.next; + Node victim = requireNonNull(headB1.next); data.remove(victim.key); victim.remove(); sizeB1--; evict(node); } else { - Node victim = headT1.next; + Node victim = requireNonNull(headT1.next); data.remove(victim.key); victim.remove(); sizeT1--; } } else if ((sizeL1 < maximumSize) && ((sizeL1 + sizeL2) >= maximumSize)) { if ((sizeL1 + sizeL2) >= (2 * maximumSize)) { - Node victim = headB2.next; + Node victim = requireNonNull(headB2.next); data.remove(victim.key); victim.remove(); sizeB2--; @@ -197,14 +198,14 @@ private void evict(Node candidate) { // else move the LRU page in T2 to the top of B2 and remove it from the cache. if ((sizeT1 >= 1) && (((candidate.type == QueueType.B2) && (sizeT1 == p)) || (sizeT1 > p))) { - Node victim = headT1.next; + Node victim = requireNonNull(headT1.next); victim.remove(); victim.type = QueueType.B1; victim.appendToTail(headB1); sizeT1--; sizeB1++; } else { - Node victim = headT2.next; + Node victim = requireNonNull(headT2.next); victim.remove(); victim.type = QueueType.B2; victim.appendToTail(headB2); @@ -255,7 +256,7 @@ static final class Node { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -264,6 +265,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CarPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CarPolicy.java index 7361fc94b2..91d0462a81 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CarPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CarPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.adaptive; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -124,13 +125,13 @@ private void onMiss(long key, @Var Node node) { if (!isGhost(node)) { if ((sizeT1 + sizeB1) == maximumSize) { // Discard the LRU page in B1 - Node victim = headB1.next; + Node victim = requireNonNull(headB1.next); data.remove(victim.key); victim.remove(); sizeB1--; } else if ((sizeT1 + sizeT2 + sizeB1 + sizeB2) == (2 * maximumSize)) { // Discard the LRU page in B2 - Node victim = headB2.next; + Node victim = requireNonNull(headB2.next); data.remove(victim.key); victim.remove(); sizeB2--; @@ -199,7 +200,7 @@ private void demote() { for (;;) { policyStats.recordOperation(); if (sizeT1 >= Math.max(1, p)) { - Node candidate = headT1.next; + Node candidate = requireNonNull(headT1.next); if (!candidate.marked) { candidate.remove(); sizeT1--; @@ -216,7 +217,7 @@ private void demote() { sizeT2++; } } else { - Node candidate = headT2.next; + Node candidate = requireNonNull(headT2.next); if (!candidate.marked) { candidate.remove(); sizeT2--; @@ -275,7 +276,7 @@ static final class Node { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -284,6 +285,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CartPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CartPolicy.java index c459e217ef..537adc1c82 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CartPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/adaptive/CartPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.adaptive; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -136,13 +137,13 @@ private void onMiss(long key, @Var Node node) { if (!isGhost(node) && ((sizeB1 + sizeB2) == (maximumSize + 1))) { if (sizeB1 > Math.max(0, q) || (sizeB2 == 0)) { // Remove the bottom page in B1 from the history - Node victim = headB1.next; + Node victim = requireNonNull(headB1.next); data.remove(victim.key); victim.remove(); sizeB1--; } else { // Remove the bottom page in B2 from the history - Node victim = headB2.next; + Node victim = requireNonNull(headB2.next); data.remove(victim.key); victim.remove(); sizeB2--; @@ -241,7 +242,7 @@ private void demote() { policyStats.recordEviction(); - while (headT2.next.marked) { + while (requireNonNull(headT2.next).marked) { policyStats.recordOperation(); Node demoted = headT2.next; demoted.marked = false; @@ -256,7 +257,8 @@ private void demote() { } } - while ((headT1.next.filter == FilterType.LONG_TERM) || headT1.next.marked) { + while ((requireNonNull(headT1.next).filter == FilterType.LONG_TERM) + || requireNonNull(headT1.next).marked) { policyStats.recordOperation(); Node node = headT1.next; if (node.marked) { @@ -345,7 +347,7 @@ static final class Node { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -354,6 +356,10 @@ public void appendToTail(Node head) { /** Moves the node to the tail. */ public void moveToTail(Node head) { + requireNonNull(head.prev); + requireNonNull(prev); + requireNonNull(next); + // unlink prev.next = next; next.prev = prev; @@ -367,6 +373,9 @@ public void moveToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/CampPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/CampPolicy.java index 587830ba25..b7eaa83fc0 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/CampPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/CampPolicy.java @@ -17,6 +17,7 @@ import static com.github.benmanes.caffeine.cache.simulator.policy.Policy.Characteristic.WEIGHTED; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import java.util.NavigableSet; import java.util.Objects; @@ -157,7 +158,7 @@ private void onMiss(AccessEvent event) { private void evict() { var sentinel = priorityQueue.first(); - var victim = sentinel.next; + var victim = requireNonNull(sentinel.next); data.remove(victim.key); size -= victim.weight; victim.remove(); @@ -202,7 +203,7 @@ public boolean isEmpty() { /** Appends the node to the tail of the list. */ public void appendToTail(Node node) { - var tail = prev; + var tail = requireNonNull(prev); prev = node; tail.next = node; node.next = this; @@ -245,7 +246,7 @@ public String toString() { private static class Node { final long key; - Sentinel sentinel; + @Nullable Sentinel sentinel; @Nullable Node prev; @Nullable Node next; @@ -265,6 +266,9 @@ public Node(long key, int weight, Sentinel sentinel) { /** Removes the node from the list. */ public void remove() { checkState(!(this instanceof Sentinel)); + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; @@ -272,13 +276,16 @@ public void remove() { /** Moves the node to the tail. */ public void moveToTail() { + requireNonNull(prev); + requireNonNull(next); + // unlink prev.next = next; next.prev = prev; // link - next = sentinel; - prev = sentinel.prev; + next = requireNonNull(sentinel); + prev = requireNonNull(sentinel.prev); sentinel.prev = this; prev.next = this; } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/GDWheelPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/GDWheelPolicy.java index 01079c4cc5..8e6dc2b234 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/GDWheelPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/greedy_dual/GDWheelPolicy.java @@ -17,6 +17,7 @@ import static com.github.benmanes.caffeine.cache.simulator.policy.Policy.Characteristic.WEIGHTED; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -103,7 +104,7 @@ private void evict(AccessEvent event) { int hand = findNextQueue(); if (hand >= 0) { // Evict q at the tail of the C[1]th queue in level 1 Cost Wheel - var victim = wheel[0][hand].prev; + var victim = requireNonNull(wheel[0][hand].prev); policyStats.recordEviction(); remove(victim); } else { @@ -140,7 +141,7 @@ private void migrate(int level) { var sentinel = wheel[level][hand]; if (!sentinel.isEmpty()) { // Remove p - var node = sentinel.next; + var node = requireNonNull(sentinel.next); node.remove(); // Cost Remainder ← c(p) mod NQ^(idx-1) @@ -204,7 +205,7 @@ public void finished() { for (var sentinel : costWheel) { @Var Node next = sentinel.next; while (next != sentinel) { - next = next.next; + next = requireNonNull(next).next; nodes++; } } @@ -235,6 +236,7 @@ public boolean isEmpty() { /** Appends the node to the head of the list. */ public void appendToHead(Node node) { + requireNonNull(next); node.prev = this; node.next = next; next.prev = node; @@ -266,6 +268,8 @@ public Node(long key) { /** Removes the node from the list. */ public void remove() { checkState(!(this instanceof Sentinel)); + requireNonNull(prev); + requireNonNull(next); prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPlusPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPlusPolicy.java index e684a0ae14..b618a5f931 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPlusPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPlusPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.irr; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -268,6 +269,7 @@ private boolean canPromote(Node candidate) { private void runHandCold() { // runHandCold is used to search for a resident cold page for replacement. + requireNonNull(handCold); checkState(handCold.isResidentCold()); if (handCold.marked) { @@ -322,6 +324,7 @@ private boolean runHandHot(Node trigger) { // What triggers the movement of handHot is that a cold page (== argument "trigger") is found to // have been accessed in its test period and thus turns into a hot page, which "maybe" // accordingly turns the hot page with the largest recency into a cold page. + requireNonNull(handHot); checkState(handHot.isHot()); checkState(trigger.isInTest()); @@ -364,6 +367,7 @@ private boolean runHandHot(Node trigger) { } private void runHandTest() { + requireNonNull(handTest); checkState(handTest.isInTest()); terminateTestPeriod(handTest); nextHandTest(); @@ -389,10 +393,10 @@ private static void terminateTestPeriod(Node node) { private void nextHandCold() { if (sizeResCold > 0) { if (handCold == null) { - handCold = listHead.prev; + handCold = requireNonNull(listHead).prev; } while (!handCold.isResidentCold()) { - handCold = handCold.prev; + handCold = requireNonNull(handCold).prev; } } else { handCold = null; @@ -403,7 +407,7 @@ private void nextHandCold() { private void nextHandHot() { if (sizeHot > 0) { if (handHot == null) { - handHot = listHead.prev; + handHot = requireNonNull(listHead).prev; } while (handHot.isCold()) { // Terminate test period of encountered cold pages. @@ -421,7 +425,7 @@ private void nextHandHot() { private void nextHandTest() { if (sizeInTest > 0) { if (handTest == null) { - handTest = (handHot == null ? listHead.prev : handHot); + handTest = (handHot == null ? requireNonNull(listHead).prev : handHot); } while (!handTest.isInTest()) { handTest = handTest.prev; @@ -547,6 +551,7 @@ private void validateStatus() { private void printClock() { System.out.println("** CLOCK-Pro list HEAD (small recency) **"); System.out.println(listHead); + requireNonNull(listHead); for (Node n = listHead.next; n != listHead; n = n.next) { System.out.println(n); } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPolicy.java index b57aee8620..38f305e5a2 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.irr; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -246,6 +247,7 @@ private boolean canPromote(Node candidate) { private void runHandCold() { // runHandCold is used to search for a resident cold page for replacement. + requireNonNull(handCold); checkState(handCold.isResidentCold()); if (handCold.marked) { @@ -290,6 +292,7 @@ private boolean runHandHot(Node trigger) { // What triggers the movement of handHot is that a cold page (== argument "trigger") is found to // have been accessed in its test period and thus turns into a hot page, which "maybe" // accordingly turns the hot page with the largest recency into a cold page. + requireNonNull(handHot); checkState(handHot.isHot()); checkState(trigger.isInTest()); @@ -324,6 +327,7 @@ private boolean runHandHot(Node trigger) { } private void runHandTest() { + requireNonNull(handTest); checkState(handTest.isInTest()); terminateTestPeriod(handTest); nextHandTest(); @@ -351,7 +355,7 @@ private void terminateTestPeriod(Node node) { private void nextHandCold() { if (sizeResCold > 0) { if (handCold == null) { - handCold = listHead.prev; + handCold = requireNonNull(listHead).prev; } while (!handCold.isResidentCold()) { handCold = handCold.prev; @@ -365,7 +369,7 @@ private void nextHandCold() { private void nextHandHot() { if (sizeHot > 0) { if (handHot == null) { - handHot = listHead.prev; + handHot = requireNonNull(listHead).prev; } while (handHot.isCold()) { // Terminate test period of encountered cold pages. @@ -383,7 +387,7 @@ private void nextHandHot() { private void nextHandTest() { if (sizeInTest > 0) { if (handTest == null) { - handTest = (handHot == null ? listHead.prev : handHot); + handTest = (handHot == null ? requireNonNull(listHead).prev : handHot); } while (!handTest.isInTest()) { handTest = handTest.prev; @@ -484,6 +488,7 @@ private void validateStatus() { private void printClock() { System.out.println("** CLOCK-Pro list HEAD (small recency) **"); System.out.println(listHead); + requireNonNull(listHead); for (Node n = listHead.next; n != listHead; n = n.next) { System.out.println(n); } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProSimplePolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProSimplePolicy.java index 1088a3b1cd..6b4d3135c8 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProSimplePolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/ClockProSimplePolicy.java @@ -17,6 +17,8 @@ import static com.google.common.base.Preconditions.checkState; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -362,7 +364,7 @@ static final class Node { final long key; long epoch; - Status status; + @Nullable Status status; Node prev; Node next; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/DClockPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/DClockPolicy.java index d5309d5df2..3e56397e45 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/DClockPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/DClockPolicy.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.List; @@ -128,7 +129,7 @@ private void onInactiveHit(Node node) { private void activate(Node node) { activeSize++; if (activeSize > maxActive) { - Node demote = headActive.next; + Node demote = requireNonNull(headActive.next); inactiveSize++; demote.remove(); demote.status = Status.INACTIVE; @@ -176,7 +177,7 @@ private void evict() { int residentSize = (inactiveSize + activeSize); if (residentSize > maximumSize) { - Node victim = headInactive.prev; + Node victim = requireNonNull(headInactive.prev); policyStats.recordEviction(); evictions++; @@ -195,7 +196,7 @@ private void prune() { // than that are dismissed. int nonResidentSize = data.size() - maximumSize; if (nonResidentSize > maxActive) { - Node node = headNonResident.prev; + Node node = requireNonNull(headNonResident.prev); data.remove(node.key); node.remove(); } @@ -269,7 +270,7 @@ public Node(long key, Status status) { /** Appends the node to the head of the list. */ public void appendToHead(Node head) { - Node first = head.next; + Node first = requireNonNull(head.next); first.prev = this; head.next = this; next = first; @@ -280,11 +281,13 @@ public void appendToHead(Node head) { public void moveToHead(Node head) { // unlink if (prev != null) { + requireNonNull(next); prev.next = next; next.prev = prev; } // link + requireNonNull(head.next); prev = head; next = head.next; prev.next = this; @@ -293,6 +296,9 @@ public void moveToHead(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/FrdPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/FrdPolicy.java index 9be61f96ee..0bb6d3e069 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/FrdPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/FrdPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.irr; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -125,7 +126,7 @@ private void onFullMiss(Node node) { // reuse distance stack because the history block contains only metadata. policyStats.recordEviction(); - Node victim = headFilter.prevFilter; + Node victim = requireNonNull(headFilter.prevFilter); victim.removeFrom(StackType.FILTER); if (victim.isInMain) { victim.status = Status.NON_RESIDENT; @@ -165,7 +166,7 @@ private void onMainHit(Node node) { private void pruneStack() { for (;;) { - Node bottom = headMain.prevMain; + Node bottom = requireNonNull(headMain.prevMain); if ((bottom == headMain) || (bottom.status == Status.MAIN)) { break; } else if (bottom.status == Status.FILTER) { @@ -188,7 +189,7 @@ private void onNonResidentHit(Node node) { policyStats.recordMiss(); pruneStack(); - Node victim = headMain.prevMain; + Node victim = requireNonNull(headMain.prevMain); victim.removeFrom(StackType.MAIN); data.remove(victim.key); pruneStack(); @@ -268,7 +269,7 @@ public void moveToTop(StackType stackType) { switch (stackType) { case FILTER: { - Node next = headFilter.nextFilter; + Node next = requireNonNull(headFilter.nextFilter); headFilter.nextFilter = this; next.prevFilter = this; this.nextFilter = next; @@ -277,7 +278,7 @@ public void moveToTop(StackType stackType) { return; } case MAIN: { - Node next = headMain.nextMain; + Node next = requireNonNull(headMain.nextMain); headMain.nextMain = this; next.prevMain = this; this.nextMain = next; @@ -295,6 +296,9 @@ public void removeFrom(StackType stackType) { switch (stackType) { case FILTER: { + requireNonNull(prevFilter); + requireNonNull(nextFilter); + prevFilter.nextFilter = nextFilter; nextFilter.prevFilter = prevFilter; prevFilter = nextFilter = null; @@ -302,6 +306,9 @@ public void removeFrom(StackType stackType) { return; } case MAIN: { + requireNonNull(prevMain); + requireNonNull(nextMain); + prevMain.nextMain = nextMain; nextMain.prevMain = prevMain; prevMain = nextMain = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/HillClimberFrdPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/HillClimberFrdPolicy.java index 087ffb3ae3..a98807a28a 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/HillClimberFrdPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/HillClimberFrdPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.irr; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -160,7 +161,7 @@ private void adaptMainToFilter(Node node) { policyStats.recordEviction(); pruneStack(); - Node victim = headMain.prevMain; + Node victim = requireNonNull(headMain.prevMain); victim.removeFrom(StackType.MAIN); data.remove(victim.key); pruneStack(); @@ -182,7 +183,7 @@ private void adaptFilterToMain(Node node) { missesInSample++; sample++; - Node victim = headFilter.prevFilter; + Node victim = requireNonNull(headFilter.prevFilter); victim.removeFrom(StackType.FILTER); if (victim.isInMain) { victim.status = Status.NON_RESIDENT; @@ -216,7 +217,7 @@ private void onFullMiss(Node node) { */ policyStats.recordEviction(); - Node victim = headFilter.prevFilter; + Node victim = requireNonNull(headFilter.prevFilter); victim.removeFrom(StackType.FILTER); if (victim.isInMain) { victim.status = Status.NON_RESIDENT; @@ -264,7 +265,7 @@ private void onMainHit(Node node) { private void pruneStack() { for (;;) { - Node bottom = headMain.prevMain; + Node bottom = requireNonNull(headMain.prevMain); if ((bottom == headMain) || (bottom.status == Status.MAIN)) { break; } else if (bottom.status == Status.FILTER) { @@ -291,7 +292,7 @@ private void onNonResidentHit(Node node) { sample++; pruneStack(); - Node victim = headMain.prevMain; + Node victim = requireNonNull(headMain.prevMain); victim.removeFrom(StackType.MAIN); data.remove(victim.key); pruneStack(); @@ -364,7 +365,7 @@ public void moveToTop(StackType stackType) { switch (stackType) { case FILTER: { - Node next = headFilter.nextFilter; + Node next = requireNonNull(headFilter.nextFilter); headFilter.nextFilter = this; next.prevFilter = this; this.nextFilter = next; @@ -373,7 +374,7 @@ public void moveToTop(StackType stackType) { break; } case MAIN: { - Node next = headMain.nextMain; + Node next = requireNonNull(headMain.nextMain); headMain.nextMain = this; next.prevMain = this; this.nextMain = next; @@ -391,6 +392,9 @@ public void removeFrom(StackType stackType) { switch (stackType) { case FILTER: { + requireNonNull(prevFilter); + requireNonNull(nextFilter); + prevFilter.nextFilter = nextFilter; nextFilter.prevFilter = prevFilter; prevFilter = nextFilter = null; @@ -398,6 +402,9 @@ public void removeFrom(StackType stackType) { break; } case MAIN: { + requireNonNull(prevMain); + requireNonNull(nextMain); + prevMain.nextMain = nextMain; nextMain.prevMain = prevMain; prevMain = nextMain = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/IndicatorFrdPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/IndicatorFrdPolicy.java index c1d829fc5c..22d92a4af6 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/IndicatorFrdPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/IndicatorFrdPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.irr; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -137,7 +138,7 @@ private void adaptMainToFilter(Node node) { policyStats.recordEviction(); pruneStack(); - Node victim = headMain.prevMain; + Node victim = requireNonNull(headMain.prevMain); victim.removeFrom(StackType.MAIN); data.remove(victim.key); pruneStack(); @@ -157,7 +158,7 @@ private void adaptFilterToMain(Node node) { policyStats.recordEviction(); policyStats.recordMiss(); - Node victim = headFilter.prevFilter; + Node victim = requireNonNull(headFilter.prevFilter); victim.removeFrom(StackType.FILTER); if (victim.isInMain) { victim.status = Status.NON_RESIDENT; @@ -191,7 +192,7 @@ private void onFullMiss(Node node) { */ policyStats.recordEviction(); - Node victim = headFilter.prevFilter; + Node victim = requireNonNull(headFilter.prevFilter); victim.removeFrom(StackType.FILTER); if (victim.isInMain) { victim.status = Status.NON_RESIDENT; @@ -235,7 +236,7 @@ private void onMainHit(Node node) { private void pruneStack() { for (;;) { - Node bottom = headMain.prevMain; + Node bottom = requireNonNull(headMain.prevMain); if ((bottom == headMain) || (bottom.status == Status.MAIN)) { break; } else if (bottom.status == Status.FILTER) { @@ -260,7 +261,7 @@ private void onNonResidentHit(Node node) { policyStats.recordMiss(); pruneStack(); - Node victim = headMain.prevMain; + Node victim = requireNonNull(headMain.prevMain); victim.removeFrom(StackType.MAIN); data.remove(victim.key); pruneStack(); @@ -333,7 +334,7 @@ public void moveToTop(StackType stackType) { switch (stackType) { case FILTER: { - Node next = headFilter.nextFilter; + Node next = requireNonNull(headFilter.nextFilter); headFilter.nextFilter = this; next.prevFilter = this; this.nextFilter = next; @@ -342,7 +343,7 @@ public void moveToTop(StackType stackType) { return; } case MAIN: { - Node next = headMain.nextMain; + Node next = requireNonNull(headMain.nextMain); headMain.nextMain = this; next.prevMain = this; this.nextMain = next; @@ -360,6 +361,9 @@ public void removeFrom(StackType stackType) { switch (stackType) { case FILTER: { + requireNonNull(prevFilter); + requireNonNull(nextFilter); + prevFilter.nextFilter = nextFilter; nextFilter.prevFilter = prevFilter; prevFilter = nextFilter = null; @@ -367,6 +371,9 @@ public void removeFrom(StackType stackType) { return; } case MAIN: { + requireNonNull(prevMain); + requireNonNull(nextMain); + prevMain.nextMain = nextMain; nextMain.prevMain = prevMain; prevMain = nextMain = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/LirsPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/LirsPolicy.java index 38d52c5b8f..8e4700c14e 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/LirsPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/irr/LirsPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.irr; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import java.util.ArrayList; import java.util.List; @@ -144,7 +145,7 @@ private void onResidentHir(Node node) { node.status = Status.LIR; node.removeFrom(StackType.Q); - Node bottom = headS.prevS; + Node bottom = requireNonNull(headS.prevS); sizeHot--; bottom.status = Status.HIR_RESIDENT; @@ -211,7 +212,7 @@ private void onFullMiss(Node node) { node.status = Status.LIR; sizeHot++; - Node bottom = headS.prevS; + Node bottom = requireNonNull(headS.prevS); checkState(bottom.status == Status.LIR); bottom.status = Status.HIR_RESIDENT; @@ -233,7 +234,7 @@ private void pruneStack() { // located above it will not have a chance to change their status from HIR to LIR since their // recencies are larger than the new maximum recency of the LIR blocks. for (;;) { - Node bottom = headS.prevS; + Node bottom = requireNonNull(headS.prevS); if ((bottom == headS) || (bottom.status == Status.LIR)) { break; } else if (bottom.status == Status.HIR_NON_RESIDENT) { @@ -250,7 +251,8 @@ private void pruneStack() { @Var Node node = headNR.prevNR; while (sizeNR > maximumNonResidentSize) { policyStats.recordOperation(); - Node removed = node; + + Node removed = requireNonNull(node); node = node.prevNR; removed.removeFrom(StackType.NR); @@ -268,7 +270,7 @@ private void evict() { policyStats.recordEviction(); residentSize--; - Node bottom = headQ.prevQ; + Node bottom = requireNonNull(headQ.prevQ); bottom.removeFrom(StackType.Q); bottom.status = Status.HIR_NON_RESIDENT; if (bottom.isInStack(StackType.S)) { @@ -312,6 +314,7 @@ public void finished() { private void printLirs() { System.out.println("** LIRS stack TOP **"); for (Node n = headS.nextS; n != headS; n = n.nextS) { + requireNonNull(n); checkState(n.isInS); if (n.status == Status.HIR_NON_RESIDENT) { System.out.println(" " + n.key); @@ -325,6 +328,7 @@ private void printLirs() { System.out.println("\n** LIRS queue END **"); for (Node n = headQ.nextQ; n != headQ; n = n.nextQ) { + requireNonNull(n); checkState(n.isInQ); System.out.println(n.key); } @@ -416,7 +420,7 @@ public void moveToTop(StackType stackType) { switch (stackType) { case S: { - Node next = headS.nextS; + Node next = requireNonNull(headS.nextS); headS.nextS = this; next.prevS = this; this.nextS = next; @@ -426,7 +430,7 @@ public void moveToTop(StackType stackType) { return; } case Q: { - Node next = headQ.nextQ; + Node next = requireNonNull(headQ.nextQ); headQ.nextQ = this; next.prevQ = this; this.nextQ = next; @@ -436,7 +440,7 @@ public void moveToTop(StackType stackType) { return; } case NR: { - Node next = headNR.nextNR; + Node next = requireNonNull(headNR.nextNR); headNR.nextNR = this; next.prevNR = this; this.nextNR = next; @@ -454,6 +458,9 @@ public void removeFrom(StackType stackType) { switch (stackType) { case S: { + requireNonNull(prevS); + requireNonNull(nextS); + prevS.nextS = nextS; nextS.prevS = prevS; prevS = nextS = null; @@ -462,6 +469,9 @@ public void removeFrom(StackType stackType) { return; } case Q: { + requireNonNull(prevQ); + requireNonNull(nextQ); + prevQ.nextQ = nextQ; nextQ.prevQ = prevQ; prevQ = nextQ = null; @@ -470,6 +480,9 @@ public void removeFrom(StackType stackType) { return; } case NR: { + requireNonNull(prevNR); + requireNonNull(nextNR); + prevNR.nextNR = nextNR; nextNR.prevNR = prevNR; prevNR = nextNR = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/FrequentlyUsedPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/FrequentlyUsedPolicy.java index 149a1c5d73..7e9b99d280 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/FrequentlyUsedPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/FrequentlyUsedPolicy.java @@ -91,9 +91,8 @@ private void onHit(Node node) { policyStats.recordHit(); int newCount = node.freq.count + 1; - FrequencyNode freqN = (node.freq.next.count == newCount) - ? node.freq.next - : new FrequencyNode(newCount, node.freq); + var next = requireNonNull(node.freq.next); + FrequencyNode freqN = (next.count == newCount) ? next : new FrequencyNode(newCount, node.freq); node.remove(); if (node.freq.isEmpty()) { node.freq.remove(); @@ -104,9 +103,8 @@ private void onHit(Node node) { /** Adds the entry, creating an initial frequency list of 1 if necessary, and evicts if needed. */ private void onMiss(long key) { - FrequencyNode freq1 = (freq0.next.count == 1) - ? freq0.next - : new FrequencyNode(1, freq0); + var next = requireNonNull(freq0.next); + FrequencyNode freq1 = (next.count == 1) ? next : new FrequencyNode(1, freq0); var node = new Node(freq1, key); policyStats.recordMiss(); data.put(key, node); @@ -136,17 +134,18 @@ private void evict(Node candidate) { Node nextVictim(Node candidate) { if (policy == EvictionPolicy.MFU) { // highest, never the candidate - return freq0.prev.nextNode.next; + var prev = requireNonNull(freq0.prev); + return requireNonNull(prev.nextNode.next); } // find the lowest that is not the candidate - @Var Node victim = freq0.next.nextNode.next; + @Var Node victim = requireNonNull(freq0.next).nextNode.next; if (victim == candidate) { victim = (victim.next == victim.prev) - ? victim.freq.next.nextNode.next + ? requireNonNull(victim.freq.next).nextNode.next : victim.next; } - return victim; + return requireNonNull(victim); } /** Removes the entry. */ @@ -182,6 +181,7 @@ public FrequencyNode() { } public FrequencyNode(int count, FrequencyNode prev) { + requireNonNull(prev.next); nextNode = new Node(this); this.prev = prev; this.next = prev.next; @@ -196,6 +196,9 @@ public boolean isEmpty() { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; @@ -233,14 +236,17 @@ public Node(FrequencyNode freq, long key) { /** Appends the node to the tail of the list. */ public void append() { - prev = freq.nextNode.prev; - next = freq.nextNode; + prev = requireNonNull(freq.nextNode.prev); + next = requireNonNull(freq.nextNode); prev.next = this; next.prev = this; } /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/LinkedPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/LinkedPolicy.java index b801d0a630..4ab0109a02 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/LinkedPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/LinkedPolicy.java @@ -17,6 +17,7 @@ import static com.github.benmanes.caffeine.cache.simulator.policy.Policy.Characteristic.WEIGHTED; import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.Set; @@ -149,7 +150,7 @@ public enum EvictionPolicy { } @Override Node findVictim(Node sentinel, PolicyStats policyStats) { policyStats.recordOperation(); - return sentinel.next; + return requireNonNull(sentinel.next); } }, @@ -165,7 +166,7 @@ public enum EvictionPolicy { @Override Node findVictim(Node sentinel, PolicyStats policyStats) { for (;;) { policyStats.recordOperation(); - Node node = sentinel.next; + Node node = requireNonNull(sentinel.next); if (node.marked) { node.moveToTail(); node.marked = false; @@ -185,7 +186,7 @@ public enum EvictionPolicy { @Override Node findVictim(Node sentinel, PolicyStats policyStats) { policyStats.recordOperation(); // Skip over the added entry - return sentinel.prev.prev; + return requireNonNull(requireNonNull(sentinel.prev).prev); } }, @@ -197,7 +198,7 @@ public enum EvictionPolicy { } @Override Node findVictim(Node sentinel, PolicyStats policyStats) { policyStats.recordOperation(); - return sentinel.next; + return requireNonNull(sentinel.next); } }; @@ -234,13 +235,13 @@ public Node() { /** Creates a new, unlinked node. */ public Node(long key, int weight, Node sentinel) { this.sentinel = sentinel; - this.key = key; this.weight = weight; + this.key = key; } /** Appends the node to the tail of the list. */ public void appendToTail() { - Node tail = sentinel.prev; + Node tail = requireNonNull(sentinel.prev); sentinel.prev = this; tail.next = this; next = sentinel; @@ -249,6 +250,9 @@ public void appendToTail() { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; @@ -257,13 +261,16 @@ public void remove() { /** Moves the node to the tail. */ public void moveToTail() { + requireNonNull(prev); + requireNonNull(next); + // unlink prev.next = next; next.prev = prev; // link next = sentinel; - prev = sentinel.prev; + prev = requireNonNull(sentinel.prev); sentinel.prev = this; prev.next = this; } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/MultiQueuePolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/MultiQueuePolicy.java index 945bdc06d3..5514bb9946 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/MultiQueuePolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/MultiQueuePolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.linked; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import java.util.Arrays; @@ -108,7 +109,8 @@ public void record(long key) { private void adjust() { currentTime++; for (int i = 1; i < headQ.length; i++) { - Node node = headQ[i].next; + Node node = requireNonNull(headQ[i].next); + requireNonNull(node.next); if (node.next.expireTime < currentTime) { node.remove(); node.queueIndex = (i - 1); @@ -177,7 +179,7 @@ static Node sentinel(int queueIndex) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -186,6 +188,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + queueIndex = -1; prev.next = next; next.prev = prev; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/S4LruPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/S4LruPolicy.java index 8d4da717ab..0193445119 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/S4LruPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/S4LruPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.linked; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.Arrays; @@ -121,7 +122,7 @@ private void adjust() { int maxPerLevel = maximumSize / levels; for (int i = levels - 1; i > 0; i--) { if (sizeQ[i] > maxPerLevel) { - Node demote = headQ[i].next; + Node demote = requireNonNull(headQ[i].next); demote.remove(); sizeQ[i]--; @@ -136,7 +137,7 @@ private void evict(Node candidate) { if (data.size() > maximumSize) { policyStats.recordEviction(); - Node victim = headQ[0].next; + Node victim = requireNonNull(headQ[0].next); boolean admit = admittor.admit(candidate.key, victim.key); if (admit) { evictEntry(victim); @@ -177,6 +178,7 @@ static final class Node { Node(long key) { this.key = key; + prev = next = this; } static Node sentinel(int level) { @@ -189,7 +191,7 @@ static Node sentinel(int level) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -198,6 +200,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SegmentedLruPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SegmentedLruPolicy.java index 1ebbae8d97..756ff35cc3 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SegmentedLruPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SegmentedLruPolicy.java @@ -19,6 +19,8 @@ import java.util.Set; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; @@ -162,7 +164,7 @@ static final class Node { Node prev; Node next; - QueueType type; + @Nullable QueueType type; Node() { this.key = Long.MIN_VALUE; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SievePolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SievePolicy.java index 39cae27f33..da0eae149d 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SievePolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/linked/SievePolicy.java @@ -18,6 +18,8 @@ import static com.github.benmanes.caffeine.cache.simulator.policy.Policy.Characteristic.WEIGHTED; import static com.google.common.base.Preconditions.checkState; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.policy.AccessEvent; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; @@ -49,9 +51,10 @@ public final class SievePolicy implements Policy { final PolicyStats policyStats; final long maximumSize; - Node head; - Node tail; - Node hand; + @Nullable Node head; + @Nullable Node tail; + @Nullable Node hand; + long size; public SievePolicy(Config config) { @@ -156,8 +159,9 @@ public PolicyStats stats() { static final class Node { final long key; - Node prev; - Node next; + @Nullable Node prev; + @Nullable Node next; + int weight; boolean visited; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/opt/ClairvoyantPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/opt/ClairvoyantPolicy.java index 098af01c10..1a469c43a9 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/opt/ClairvoyantPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/opt/ClairvoyantPolicy.java @@ -18,6 +18,8 @@ import java.util.ArrayDeque; import java.util.Queue; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.policy.AccessEvent; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; @@ -46,7 +48,7 @@ public final class ClairvoyantPolicy implements Policy { private final IntSortedSet data; private final int maximumSize; - private Recorder recorder; + private @Nullable Recorder recorder; private int infiniteTimestamp; private int tick; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/product/CaffeinePolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/product/CaffeinePolicy.java index 58d7cacc65..402d614e72 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/product/CaffeinePolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/product/CaffeinePolicy.java @@ -19,6 +19,8 @@ import java.util.Set; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; @@ -44,7 +46,7 @@ public CaffeinePolicy(Config config, Set characteristics) { policyStats = new PolicyStats(name()); var settings = new BasicSettings(config); Caffeine builder = Caffeine.newBuilder() - .removalListener((Long key, AccessEvent value, RemovalCause cause) -> + .removalListener((@Nullable Long key, @Nullable AccessEvent value, RemovalCause cause) -> policyStats.recordEviction()) .executor(Runnable::run); if (characteristics.contains(WEIGHTED)) { diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sampled/SampledPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sampled/SampledPolicy.java index ccc2bf4bfe..297130f08e 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sampled/SampledPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sampled/SampledPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.sampled; import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.ArrayList; @@ -27,6 +28,7 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.Nullable; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.admission.Admission; @@ -56,13 +58,13 @@ public final class SampledPolicy implements KeyOnlyPolicy { final Long2ObjectMap data; final PolicyStats policyStats; + final @Nullable Node[] table; final EvictionPolicy policy; final Sample sampleStrategy; final Admittor admittor; final int maximumSize; final int sampleSize; final Random random; - final Node[] table; long tick; @@ -134,17 +136,18 @@ private void evict(Node candidate) { /** Removes the node from the table and adds the index to the free list. */ private void removeFromTable(Node node) { - int last = data.size() - 1; - table[node.index] = table[last]; - table[node.index].index = node.index; - table[last] = null; + int index = data.size() - 1; + var last = requireNonNull(table[index]); + table[node.index] = last; + last.index = node.index; + table[index] = null; } /** The algorithms to choose a random sample with. */ public enum Sample { GUESS { @SuppressWarnings("PMD.AvoidReassigningLoopVariables") - @Override public List sample(E[] elements, E candidate, + @Override public List sample(@Nullable E[] elements, E candidate, int sampleSize, Random random, PolicyStats policyStats) { var sample = new ArrayList(sampleSize); policyStats.addOperations(sampleSize); @@ -158,7 +161,7 @@ public enum Sample { } }, RESERVOIR { - @Override public List sample(E[] elements, E candidate, + @Override public List sample(@Nullable E[] elements, E candidate, int sampleSize, Random random, PolicyStats policyStats) { var sample = new ArrayList(sampleSize); policyStats.addOperations(elements.length); @@ -181,7 +184,7 @@ public enum Sample { } }, SHUFFLE { - @Override public List sample(E[] elements, E candidate, + @Override public List sample(@Nullable E[] elements, E candidate, int sampleSize, Random random, PolicyStats policyStats) { var sample = new ArrayList(Arrays.asList(elements)); policyStats.addOperations(elements.length); @@ -191,7 +194,7 @@ public enum Sample { } }; - abstract List sample(E[] elements, E candidate, + abstract List sample(@Nullable E[] elements, E candidate, int sampleSize, Random random, PolicyStats policyStats); } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java index 96d728cfaf..26aeaa2043 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.sketch; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.List; @@ -146,7 +147,7 @@ private void onProbationHit(Node node) { sizeProtected++; if (sizeProtected > maxProtected) { - Node demote = headProtected.next; + Node demote = requireNonNull(headProtected.next); demote.remove(); demote.status = Status.PROBATION; demote.appendToTail(headProbation); @@ -169,7 +170,7 @@ private void evict() { return; } - Node candidate = headWindow.next; + Node candidate = requireNonNull(headWindow.next); sizeWindow--; candidate.remove(); @@ -177,7 +178,7 @@ private void evict() { candidate.appendToTail(headProbation); if (data.size() > maximumSize) { - Node victim = headProbation.next; + Node victim = requireNonNull(headProbation.next); Node evict = admittor.admit(candidate.key, victim.key) ? victim : candidate; data.remove(evict.key); evict.remove(); @@ -231,6 +232,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { + requireNonNull(head.prev); Node tail = head.prev; head.prev = this; tail.next = this; @@ -240,6 +242,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java index f8be96c951..65839b2ed7 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Sets.toImmutableEnumSet; import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; import java.util.HashSet; import java.util.List; @@ -143,6 +144,7 @@ private void onMiss(long key) { /** Moves or promotes as if necessary. */ private void onHit(Node node) { + requireNonNull(node.queue); switch (node.queue) { case WINDOW: onWindowHit(node); @@ -176,7 +178,7 @@ private void onProbationHit(Node node) { private void demoteProtected() { if (protectedSize > maxProtected) { - Node demote = headProtected.next; + Node demote = requireNonNull(headProtected.next); demote.remove(); demote.queue = PROBATION; demote.appendToTail(headProbation); @@ -198,7 +200,7 @@ private void evict() { return; } - Node candidate = headWindow.next; + Node candidate = requireNonNull(headWindow.next); windowSize--; candidate.remove(); @@ -206,7 +208,7 @@ private void evict() { candidate.appendToTail(headProbation); if (data.size() > maximumSize) { - Node victim = headProbation.next; + Node victim = requireNonNull(headProbation.next); Node evict = admittor.admit(candidate.key, victim.key) ? victim : candidate; data.remove(evict.key); evict.remove(); @@ -247,6 +249,8 @@ private void increaseWindow(double amount) { maxProtected--; demoteProtected(); + requireNonNull(headProbation.next); + Node candidate = headProbation.next; candidate.remove(); candidate.queue = WINDOW; @@ -274,6 +278,7 @@ private void decreaseWindow(double amount) { for (int i = 0; i < steps; i++) { maxWindow--; maxProtected++; + requireNonNull(headWindow.next); Node candidate = headWindow.next; candidate.remove(); @@ -344,7 +349,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToHead(Node head) { - Node first = head.next; + Node first = requireNonNull(head.next); head.next = this; first.prev = this; prev = head; @@ -353,7 +358,7 @@ public void appendToHead(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -362,6 +367,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java index 02623a2b77..5a3d886df0 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.sketch.feedback; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import java.util.Map; @@ -128,7 +129,7 @@ private void onHit(Node node) { private void evict(Node candidate) { if (data.size() > maximumSize) { Node evict; - Node victim = head.next; + Node victim = requireNonNull(head.next); if (admittor.admit(candidate.key, victim.key)) { evict = victim; } else if (adapt(candidate)) { @@ -205,6 +206,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { + requireNonNull(head.prev); Node tail = head.prev; head.prev = this; tail.next = this; @@ -214,6 +216,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java index 310044509e..eda28b02d9 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkState; import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.List; @@ -179,7 +180,7 @@ private void onProbationHit(Node node) { private void demoteProtected() { if (sizeProtected > maxProtected) { - Node demote = headProtected.next; + Node demote = requireNonNull(headProtected.next); demote.remove(); demote.status = Status.PROBATION; demote.appendToTail(headProbation); @@ -202,7 +203,7 @@ private void evict() { return; } - Node candidate = headWindow.next; + Node candidate = requireNonNull(headWindow.next); sizeWindow--; candidate.remove(); @@ -211,7 +212,7 @@ private void evict() { if (data.size() > maximumSize) { Node evict; - Node victim = headProbation.next; + Node victim = requireNonNull(headProbation.next); if (admittor.admit(candidate.key, victim.key)) { evict = victim; } else if (adapt(candidate)) { @@ -259,6 +260,9 @@ private boolean adapt(@Var Node candidate) { maxProtected--; demoteProtected(); + requireNonNull(headProbation.next); + requireNonNull(headProbation.next.next); + candidate = headProbation.next.next; candidate.remove(); candidate.status = Status.WINDOW; @@ -284,6 +288,7 @@ private boolean adapt(@Var Node candidate) { sizeWindow--; maxProtected++; decremented = true; + requireNonNull(headWindow.next); candidate = headWindow.next; candidate.remove(); @@ -353,6 +358,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToHead(Node head) { + requireNonNull(head.next); Node first = head.next; head.next = this; first.prev = this; @@ -362,6 +368,7 @@ public void appendToHead(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { + requireNonNull(head.prev); Node tail = head.prev; head.prev = this; tail.next = this; @@ -371,6 +378,8 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java index cab36c130c..3eb71df2ec 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.sketch.segment; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.List; @@ -136,7 +137,7 @@ private void onWindowProbationHit(Node node) { sizeWindowProtected++; if (sizeWindowProtected > maxWindowProtected) { - Node demote = headWindowProtected.next; + Node demote = requireNonNull(headWindowProtected.next); demote.remove(); demote.status = Status.WINDOW_PROBATION; demote.appendToTail(headWindowProbation); @@ -157,7 +158,7 @@ private void onMainProbationHit(Node node) { sizeMainProtected++; if (sizeMainProtected > maxMainProtected) { - Node demote = headMainProtected.next; + Node demote = requireNonNull(headMainProtected.next); demote.remove(); demote.status = Status.MAIN_PROBATION; demote.appendToTail(headMainProbation); @@ -179,7 +180,7 @@ private void evict() { return; } - Node candidate = headWindowProbation.next; + Node candidate = requireNonNull(headWindowProbation.next); sizeWindow--; candidate.remove(); @@ -187,7 +188,7 @@ private void evict() { candidate.appendToTail(headMainProbation); if (data.size() > maximumSize) { - Node victim = headMainProbation.next; + Node victim = requireNonNull(headMainProbation.next); Node evict = admittor.admit(candidate.key, victim.key) ? victim : candidate; data.remove(evict.key); evict.remove(); @@ -246,6 +247,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { + requireNonNull(head.prev); Node tail = head.prev; head.prev = this; tail.next = this; @@ -255,6 +257,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java index d21f5eb9b5..12d41aa7e4 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.sketch.segment; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.List; @@ -111,7 +112,7 @@ private void evict() { return; } - Node candidate = headWindow.next; + Node candidate = requireNonNull(headWindow.next); candidate.remove(); sizeWindow--; @@ -120,7 +121,7 @@ private void evict() { sizeMain++; if (sizeMain > maxMain) { - Node victim = headMain.next; + Node victim = requireNonNull(headMain.next); Node evict = admittor.admit(candidate.key, victim.key) ? victim : candidate; data.remove(evict.key); evict.remove(); @@ -169,6 +170,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { + requireNonNull(head.prev); Node tail = head.prev; head.prev = this; tail.next = this; @@ -178,6 +180,9 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java index d86dee4b1f..11c93876ad 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java @@ -15,12 +15,15 @@ */ package com.github.benmanes.caffeine.cache.simulator.policy.sketch.segment; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.List; import java.util.Random; import java.util.Set; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; @@ -44,11 +47,11 @@ public final class RandomWindowTinyLfuPolicy implements KeyOnlyPolicy { final Long2ObjectMap data; final PolicyStats policyStats; + final @Nullable Node[] window; + final @Nullable Node[] main; final Admittor admittor; final int maximumSize; final Random random; - final Node[] window; - final Node[] main; int windowSize; int mainSize; @@ -103,7 +106,7 @@ private void evict() { return; } - Node candidate = window[random.nextInt(window.length)]; + Node candidate = requireNonNull(window[random.nextInt(window.length)]); removeFromTable(window, candidate); windowSize--; @@ -112,7 +115,7 @@ private void evict() { mainSize++; if (data.size() > maximumSize) { - Node victim = main[random.nextInt(main.length)]; + Node victim = requireNonNull(main[random.nextInt(main.length)]); Node evict = admittor.admit(candidate.key, victim.key) ? victim : candidate; removeFromTable(main, evict); data.remove(evict.key); @@ -123,11 +126,12 @@ private void evict() { } /** Removes the node from the table and adds the index to the free list. */ - private static void removeFromTable(Node[] table, Node node) { - int last = table.length - 1; - table[node.index] = table[last]; - table[node.index].index = node.index; - table[last] = null; + private static void removeFromTable(@Nullable Node[] table, Node node) { + int index = table.length - 1; + var last = requireNonNull(table[index]); + table[node.index] = last; + last.index = node.index; + table[index] = null; } /** A node on the double-linked list. */ diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java index 9cfa54db45..5182e7190f 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.sketch.segment; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toUnmodifiableSet; import java.util.Arrays; @@ -138,7 +139,7 @@ private void adjust() { int maxPerLevel = maxMain / levels; for (int i = levels - 1; i > 0; i--) { if (sizeMainQ[i] > maxPerLevel) { - Node demote = headMainQ[i].next; + Node demote = requireNonNull(headMainQ[i].next); demote.remove(); sizeMainQ[i]--; @@ -155,7 +156,7 @@ private void evict() { return; } - Node candidate = headWindow.next; + Node candidate = requireNonNull(headWindow.next); candidate.remove(); sizeWindow--; @@ -164,7 +165,7 @@ private void evict() { sizeMainQ[0]++; if (data.size() > maximumSize) { - Node victim = headMainQ[0].next; + Node victim = requireNonNull(headMainQ[0].next); Node evict = admittor.admit(candidate.key, victim.key) ? victim : candidate; data.remove(evict.key); evict.remove(); @@ -224,6 +225,7 @@ public void moveToTail(Node head) { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { + requireNonNull(head.prev); Node tail = head.prev; head.prev = this; tail.next = this; @@ -233,6 +235,8 @@ public void appendToTail(Node head) { /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); prev.next = next; next.prev = prev; next = prev = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/S3FifoPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/S3FifoPolicy.java index 4d02ad3607..195e597989 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/S3FifoPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/S3FifoPolicy.java @@ -17,6 +17,7 @@ import static com.github.benmanes.caffeine.cache.simulator.policy.Policy.Characteristic.WEIGHTED; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import java.util.LinkedHashSet; import java.util.function.IntConsumer; @@ -177,7 +178,7 @@ private void evict() { private void evictFromSmall() { @Var boolean evicted = false; while (!evicted && !dataSmall.isEmpty()) { - var victim = sentinelSmall.prev; + var victim = requireNonNull(sentinelSmall.prev); policyStats.recordOperation(); if (victim.frequency > moveToMainThreshold) { insertMain(victim.key, victim.weight); @@ -198,7 +199,7 @@ private void evictFromSmall() { private void evictFromMain() { @Var boolean evicted = false; while (!evicted && !dataMain.isEmpty()) { - var victim = sentinelMain.prev; + var victim = requireNonNull(sentinelMain.prev); policyStats.recordOperation(); if (victim.frequency > 0) { victim.moveToHead(sentinelMain); @@ -215,7 +216,7 @@ private void evictFromMain() { private void evictFromGhost() { if (!dataGhost.isEmpty()) { - var victim = sentinelGhost.prev; + var victim = requireNonNull(sentinelGhost.prev); dataGhost.remove(victim.key); sizeGhost -= victim.weight; victim.remove(); @@ -244,6 +245,7 @@ public void finished() { private static void checkLinks(String label, Long2ObjectMap data, Node sentinel) { var forwards = new LinkedHashSet(); for (var node = sentinel.next; node != sentinel; node = node.next) { + requireNonNull(node); checkState(node == data.get(node.key), "%s: %s != %s", label, node, data.get(node.key)); checkState(forwards.add(node), "%s: loop detected %s", label, forwards); } @@ -252,6 +254,7 @@ private static void checkLinks(String label, Long2ObjectMap data, Node sen var backwards = new LinkedHashSet(); for (var node = sentinel.prev; node != sentinel; node = node.prev) { + requireNonNull(node); checkState(node == data.get(node.key), "%s: %s != %s", label, node, data.get(node.key)); checkState(backwards.add(node), "%s: loop detected %s", label, backwards); } @@ -289,7 +292,7 @@ public void appendAtHead(Node sentinel) { checkState(prev == null); checkState(next == null); - Node head = sentinel.next; + Node head = requireNonNull(sentinel.next); sentinel.next = this; head.prev = this; next = head; @@ -306,7 +309,7 @@ public void moveToHead(Node sentinel) { next.prev = prev; // link - next = sentinel.next; + next = requireNonNull(sentinel.next); sentinel.next = this; next.prev = this; prev = sentinel; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TuQueuePolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TuQueuePolicy.java index 481febfb95..067b80c758 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TuQueuePolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TuQueuePolicy.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.cache.simulator.policy.two_queue; import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; import org.jspecify.annotations.Nullable; @@ -113,7 +114,7 @@ private void onHit(Node node) { sizeWarm++; if (sizeWarm > maxWarm) { - Node demoted = headWarm.next; + Node demoted = requireNonNull(headWarm.next); demoted.remove(); sizeWarm--; demoted.type = QueueType.COLD; @@ -134,7 +135,7 @@ private void onMiss(long key) { sizeHot++; if (sizeHot > maxHot) { - Node demoted = headHot.next; + Node demoted = requireNonNull(headHot.next); demoted.remove(); sizeHot--; demoted.appendToTail(headCold); @@ -146,7 +147,7 @@ private void onMiss(long key) { private void evict() { if (data.size() > maximumSize) { - Node victim = headCold.next; + Node victim = requireNonNull(headCold.next); data.remove(victim.key); victim.remove(); sizeCold--; @@ -190,7 +191,7 @@ static final class Node { /** Appends the node to the tail of the list. */ public void appendToTail(Node head) { - Node tail = head.prev; + Node tail = requireNonNull(head.prev); head.prev = this; tail.next = this; next = head; @@ -199,19 +200,25 @@ public void appendToTail(Node head) { /** Moves the node to the tail. */ public void moveToTail(Node head) { + requireNonNull(prev); + requireNonNull(next); + // unlink prev.next = next; next.prev = prev; // link next = head; - prev = head.prev; + prev = requireNonNull(head.prev); head.prev = this; prev.next = this; } /** Removes the node from the list. */ public void remove() { + requireNonNull(prev); + requireNonNull(next); + prev.next = next; next.prev = prev; prev = next = null; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TwoQueuePolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TwoQueuePolicy.java index 54e4fc66ab..2d71d514b9 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TwoQueuePolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/two_queue/TwoQueuePolicy.java @@ -15,6 +15,10 @@ */ package com.github.benmanes.caffeine.cache.simulator.policy.two_queue; +import static java.util.Objects.requireNonNull; + +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.simulator.BasicSettings; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -90,7 +94,7 @@ public void record(long key) { policyStats.recordOperation(); @Var Node node = data.get(key); if (node != null) { - switch (node.type) { + switch (requireNonNull(node.type)) { case MAIN: node.moveToTail(headMain); policyStats.recordHit(); @@ -188,7 +192,7 @@ static final class Node { Node prev; Node next; - QueueType type; + @Nullable QueueType type; Node() { this.key = Long.MIN_VALUE; diff --git a/simulator/src/test/java/com/github/benmanes/caffeine/cache/simulator/admission/bloom/MembershipTest.java b/simulator/src/test/java/com/github/benmanes/caffeine/cache/simulator/admission/bloom/MembershipTest.java index e40faf0dac..3538bb6499 100644 --- a/simulator/src/test/java/com/github/benmanes/caffeine/cache/simulator/admission/bloom/MembershipTest.java +++ b/simulator/src/test/java/com/github/benmanes/caffeine/cache/simulator/admission/bloom/MembershipTest.java @@ -26,6 +26,8 @@ import java.util.Random; import java.util.stream.IntStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -46,11 +48,11 @@ * @author ben.manes@gmail.com (Ben Manes) */ public final class MembershipTest { - static final String[] HEADERS = { "Type", "Capacity", "Insertions", "False Positives" }; - static final double EXPECTED_INSERTIONS_MULTIPLIER = 0.5; - static final double FPP = 0.03; + private static final Logger logger = LoggerFactory.getLogger(MembershipTest.class); - static final boolean display = false; + private static final String[] HEADERS = { "Type", "Capacity", "Insertions", "False Positives" }; + private static final double EXPECTED_INSERTIONS_MULTIPLIER = 0.5; + private static final double FPP = 0.03; @SuppressWarnings("Varifier") @Test(dataProvider = "filterTypes") @@ -75,11 +77,8 @@ public void bloomFilter(FilterType filterType) { assertWithMessage(filterType.toString()).that(falsePositiveRate).isLessThan(FPP + 0.2); } rows.add(row(filterType, capacity, expectedInsertions, falsePositives, falsePositiveRate)); - - } - if (display) { - printTable(rows); } + printTable(rows); } @Test(dataProvider = "ensureCapacity") @@ -150,6 +149,6 @@ private static void printTable(List rows) { for (int i = 0; i < rows.size(); i++) { data[i] = rows.get(i); } - System.out.println(FlipTable.of(HEADERS, data)); + logger.info("\n{}", FlipTable.of(HEADERS, data)); } }