Skip to content

Commit cf5f04a

Browse files
authored
Remove use of AtomicReferenceArray in RadixTreeCache (#10600)
Remove use of AtomicReferenceArray in RadixTreeCache Instead we tolerate racing to cache sub-arrays, like we do for individual values. This does mean two or more threads might create the same sub-array, with only one winning, but this should be rare with later calls picking up the same sub-array. Co-authored-by: stuart.mcculloch <stuart.mcculloch@datadoghq.com>
1 parent 27522ae commit cf5f04a

1 file changed

Lines changed: 6 additions & 24 deletions

File tree

internal-api/src/main/java/datadog/trace/api/cache/RadixTreeCache.java

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package datadog.trace.api.cache;
22

33
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
4-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
5-
import java.util.concurrent.atomic.AtomicReferenceArray;
64
import java.util.function.IntFunction;
75

86
/** Sparse cache of values associated with a small integer */
@@ -23,11 +21,11 @@ public final class RadixTreeCache<T> {
2321
private final int shift;
2422
private final int mask;
2523

26-
private final AtomicReferenceArray<Object[]> tree;
24+
private final Object[][] tree;
2725
private final IntFunction<T> mapper;
2826

2927
public RadixTreeCache(int level1, int level2, IntFunction<T> mapper, int... commonValues) {
30-
this.tree = new AtomicReferenceArray<>(level1);
28+
this.tree = new Object[level1][];
3129
this.mapper = mapper;
3230
this.level1 = level1;
3331
this.level2 = level2;
@@ -48,31 +46,15 @@ public T get(int primitive) {
4846
}
4947

5048
@SuppressWarnings("unchecked")
51-
@SuppressFBWarnings("DCN") // only interested in catching NullPointerException (see note below)
5249
private T computeIfAbsent(int prefix, int primitive) {
53-
Object[] page = tree.get(prefix);
54-
if (null == page) {
55-
try {
56-
page = new Object[level2];
57-
if (!tree.compareAndSet(prefix, null, page)) {
58-
page = tree.get(prefix);
59-
}
60-
} catch (NullPointerException e) {
61-
// Intermittent NPE observed in JDK code on Semeru 11.0.29: java.lang.NullPointerException:
62-
// at j.l.i.ArrayVarHandle$...Operations$OpObject.computeOffset(ArrayVarHandle.java:142)
63-
// at j.l.i.ArrayVarHandle$...Operations$OpObject.compareAndSet(ArrayVarHandle.java:201)
64-
// at j.u.c.atomic.AtomicReferenceArray.compareAndSet(AtomicReferenceArray.java:152)
65-
// at datadog.trace.api.cache.RadixTreeCache.computeIfAbsent(RadixTreeCache.java:59)
66-
// Location indicates JDK's VarHandle used to access the backing array has returned null
67-
// To mitigate this rare JDK bug we still map the primitive but skip caching the result
68-
return mapper.apply(primitive);
69-
}
50+
Object[] page = tree[prefix];
51+
if (page == null) {
52+
page = tree[prefix] = new Object[level2]; // tolerate race to cache sub-array
7053
}
71-
// it's safe to race here
7254
int suffix = primitive & mask;
7355
Object cached = page[suffix];
7456
if (cached == null) {
75-
cached = page[suffix] = mapper.apply(primitive);
57+
cached = page[suffix] = mapper.apply(primitive); // tolerate race to cache value
7658
}
7759
return (T) cached;
7860
}

0 commit comments

Comments
 (0)