• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ben-manes / caffeine / #5612

11 Jul 2026 03:47PM UTC coverage: 71.292% (-28.7%) from 100.0%
#5612

push

github

ben-manes
Preserve timestamps on a raced same-instance refresh

The automatic refreshIfNeeded path checked for an equal-value reload
before the ABA/ownership check and returned without preserving the
timestamps, so a reload completing after a concurrent put(k,
sameInstance) did a full update — shifting the entry's expiration
forward by the reload's in-flight duration and stomping the write.
Mirror the explicit-refresh fix: check ownership first. An owned
reload installs the value (refreshing metadata even for a same
instance), while a raced reload preserves the write's timestamps and
only notifies REPLACED when the value differs.

2957 of 4134 branches covered (71.53%)

3 of 3 new or added lines in 1 file covered. (100.0%)

2408 existing lines in 53 files now uncovered.

5980 of 8388 relevant lines covered (71.29%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.17
/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Node.java
1
/*
2
 * Copyright 2015 Ben Manes. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.github.benmanes.caffeine.cache;
17

18
import static java.util.Locale.US;
19

20
import java.lang.ref.ReferenceQueue;
21

22
import org.jspecify.annotations.Nullable;
23

24
import com.github.benmanes.caffeine.cache.AccessOrderDeque.AccessOrder;
25
import com.github.benmanes.caffeine.cache.WriteOrderDeque.WriteOrder;
26
import com.google.errorprone.annotations.concurrent.GuardedBy;
27

28
/**
29
 * An entry in the cache containing the key, value, weight, access, and write metadata. The key
30
 * or value may be held weakly or softly requiring identity comparison.
31
 *
32
 * @author ben.manes@gmail.com (Ben Manes)
33
 */
34
@SuppressWarnings({"EmptyMethod", "unused"})
35
abstract class Node<K, V> implements AccessOrder<Node<K, V>>, WriteOrder<Node<K, V>> {
1✔
36

37
  /** Return the key or {@code null} if it has been reclaimed by the garbage collector. */
38
  public abstract @Nullable K getKey();
39

40
  /**
41
   * Returns the reference that the cache is holding the entry by. This is either the key if
42
   * strongly held or a {@link java.lang.ref.WeakReference} to that key.
43
   */
44
  public abstract Object getKeyReference();
45

46
  /**
47
   * Returns the reference that the cache is holding the entry by or {@code null} if the underlying
48
   * key has been reclaimed by the garbage collector.
49
   */
50
  public abstract @Nullable Object getKeyReferenceOrNull();
51

52
  /** Return the value or {@code null} if it has been reclaimed by the garbage collector. */
53
  public abstract @Nullable V getValue();
54

55
  /**
56
   * Returns the reference to the value. This is either the value if strongly held or a
57
   * {@link java.lang.ref.Reference} to that value.
58
   */
59
  public abstract Object getValueReference();
60

61
  /** Sets the value, which may be held strongly, weakly, or softly. */
62
  @GuardedBy("this")
63
  public abstract void setValue(V value, @Nullable ReferenceQueue<V> referenceQueue);
64

65
  /**
66
   * Returns {@code true} if the given objects are considered equivalent. A strongly held value is
67
   * compared by equality and a weakly or softly held value is compared by identity.
68
   */
69
  public abstract boolean containsValue(Object value);
70

71
  /** Returns the weight of this entry from the entry's perspective. */
72
  @GuardedBy("this")
73
  public int getWeight() {
74
    return 1;
1✔
75
  }
76

77
  /** Sets the weight from the entry's perspective. */
78
  @GuardedBy("this")
79
  public void setWeight(int weight) {}
1✔
80

81
  /** Returns the weight of this entry from the policy's perspective. */
82
  // @GuardedBy("evictionLock")
83
  public int getPolicyWeight() {
84
    return 1;
1✔
85
  }
86

87
  /** Sets the weight from the policy's perspective. */
88
  // @GuardedBy("evictionLock")
89
  public void setPolicyWeight(int weight) {}
1✔
90

91
  /* --------------- Health --------------- */
92

93
  /** If the entry is available in the hash-table and page replacement policy. */
94
  public abstract boolean isAlive();
95

96
  /**
97
   * If the entry was removed from the hash-table and is awaiting removal from the page
98
   * replacement policy.
99
   */
100
  @GuardedBy("this")
101
  public abstract boolean isRetired();
102

103
  /** If the entry was removed from the hash-table and the page replacement policy. */
104
  @GuardedBy("this")
105
  public abstract boolean isDead();
106

107
  /** Sets the node to the <code>retired</code> state. */
108
  @GuardedBy("this")
109
  public abstract void retire();
110

111
  /** Sets the node to the <code>dead</code> state. */
112
  @GuardedBy("this")
113
  public abstract void die();
114

115
  /* --------------- Variable order --------------- */
116

117
  /** Returns the variable expiration time, in nanoseconds. */
118
  public long getVariableTime() {
119
    return 0L;
1✔
120
  }
121

122
  /**
123
   * Sets the variable expiration time in nanoseconds. This update may be set lazily and rely on the
124
   * memory fence when the lock is released.
125
   */
126
  public void setVariableTime(long time) {}
1✔
127

128
  /**
129
   * Atomically sets the variable time to the given updated value if the current value equals the
130
   * expected value and returns if the update was successful.
131
   */
132
  @SuppressWarnings("UnusedReturnValue")
133
  public boolean casVariableTime(long expect, long update) {
UNCOV
134
    throw new UnsupportedOperationException();
×
135
  }
136

137
  // @GuardedBy("evictionLock")
138
  public Node<K, V> getPreviousInVariableOrder() {
UNCOV
139
    throw new UnsupportedOperationException();
×
140
  }
141

142
  // @GuardedBy("evictionLock")
143
  public void setPreviousInVariableOrder(@Nullable Node<K, V> prev) {
UNCOV
144
    throw new UnsupportedOperationException();
×
145
  }
146

147
  // @GuardedBy("evictionLock")
148
  public Node<K, V> getNextInVariableOrder() {
UNCOV
149
    throw new UnsupportedOperationException();
×
150
  }
151

152
  // @GuardedBy("evictionLock")
153
  public void setNextInVariableOrder(@Nullable Node<K, V> prev) {
UNCOV
154
    throw new UnsupportedOperationException();
×
155
  }
156

157
  /* --------------- Access order --------------- */
158

159
  public static final int WINDOW = 0;
160
  public static final int PROBATION = 1;
161
  public static final int PROTECTED = 2;
162

163
  /** Returns if the entry is in the Window or Main space. */
164
  public boolean inWindow() {
165
    return getQueueType() == WINDOW;
1✔
166
  }
167

168
  /** Returns if the entry is in the Main space's probation queue. */
169
  public boolean inMainProbation() {
170
    return getQueueType() == PROBATION;
1✔
171
  }
172

173
  /** Returns if the entry is in the Main space's protected queue. */
174
  public boolean inMainProtected() {
175
    return getQueueType() == PROTECTED;
1✔
176
  }
177

178
  /** Sets the status to the Window queue. */
179
  public void makeWindow() {
180
    setQueueType(WINDOW);
1✔
181
  }
1✔
182

183
  /** Sets the status to the Main space's probation queue. */
184
  public void makeMainProbation() {
185
    setQueueType(PROBATION);
1✔
186
  }
1✔
187

188
  /** Sets the status to the Main space's protected queue. */
189
  public void makeMainProtected() {
190
    setQueueType(PROTECTED);
1✔
191
  }
1✔
192

193
  /** Returns the queue that the entry's resides in (window, probation, or protected). */
194
  public int getQueueType() {
195
    return WINDOW;
1✔
196
  }
197

198
  /** Set queue that the entry resides in (window, probation, or protected). */
199
  public void setQueueType(int queueType) {
UNCOV
200
    throw new UnsupportedOperationException();
×
201
  }
202

203
  /** Returns the time that this entry was last accessed, in ns. */
204
  public long getAccessTime() {
205
    return 0L;
1✔
206
  }
207

208
  /**
209
   * Sets the access time in nanoseconds. This update may be set lazily and rely on the memory fence
210
   * when the lock is released.
211
   */
212
  public void setAccessTime(long time) {}
1✔
213

214
  @Override
215
  // @GuardedBy("evictionLock")
216
  public @Nullable Node<K, V> getPreviousInAccessOrder() {
217
    return null;
1✔
218
  }
219

220
  @Override
221
  // @GuardedBy("evictionLock")
222
  public void setPreviousInAccessOrder(@Nullable Node<K, V> prev) {
UNCOV
223
    throw new UnsupportedOperationException();
×
224
  }
225

226
  @Override
227
  // @GuardedBy("evictionLock")
228
  public @Nullable Node<K, V> getNextInAccessOrder() {
229
    return null;
1✔
230
  }
231

232
  @Override
233
  // @GuardedBy("evictionLock")
234
  public void setNextInAccessOrder(@Nullable Node<K, V> next) {
UNCOV
235
    throw new UnsupportedOperationException();
×
236
  }
237

238
  /* --------------- Write order --------------- */
239

240
  /** Returns the time that this entry was last written, in ns. */
241
  public long getWriteTime() {
242
    return 0L;
1✔
243
  }
244

245
  /**
246
   * Sets the write-time in nanoseconds. This update may be set lazily and rely on the memory fence
247
   * when the lock is released.
248
   */
249
  public void setWriteTime(long time) {}
1✔
250

251
  /**
252
   * Atomically sets the write-time to the given updated value if the current value equals the
253
   * expected value and returns if the update was successful.
254
   */
255
  public boolean casWriteTime(long expect, long update) {
UNCOV
256
    throw new UnsupportedOperationException();
×
257
  }
258

259
  @Override
260
  // @GuardedBy("evictionLock")
261
  public @Nullable Node<K, V> getPreviousInWriteOrder() {
262
    return null;
1✔
263
  }
264

265
  @Override
266
  // @GuardedBy("evictionLock")
267
  public void setPreviousInWriteOrder(@Nullable Node<K, V> prev) {
UNCOV
268
    throw new UnsupportedOperationException();
×
269
  }
270

271
  @Override
272
  // @GuardedBy("evictionLock")
273
  public @Nullable Node<K, V> getNextInWriteOrder() {
274
    return null;
1✔
275
  }
276

277
  @Override
278
  // @GuardedBy("evictionLock")
279
  public void setNextInWriteOrder(@Nullable Node<K, V> next) {
UNCOV
280
    throw new UnsupportedOperationException();
×
281
  }
282

283
  @Override
284
  @SuppressWarnings("GuardedBy")
285
  public final String toString() {
286
    return String.format(US, "%s=[key=%s, value=%s, weight=%d, queueType=%,d, accessTimeNS=%,d, "
1✔
287
        + "writeTimeNS=%,d, varTimeNs=%,d, prevInAccess=%s, nextInAccess=%s, prevInWrite=%s, "
288
        + "nextInWrite=%s]", getClass().getSimpleName(), getKey(), getValue(), getWeight(),
1✔
289
        getQueueType(), getAccessTime(), getWriteTime(), getVariableTime(),
1✔
290
        getPreviousInAccessOrder() != null, getNextInAccessOrder() != null,
1✔
291
        getPreviousInWriteOrder() != null, getNextInWriteOrder() != null);
1✔
292
  }
293
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc