Skip to content

Commit 7403c1a

Browse files
committed
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.
1 parent ea612c8 commit 7403c1a

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 */
@@ -25,11 +23,11 @@ public final class RadixTreeCache<T> {
2523
private final int shift;
2624
private final int mask;
2725

28-
private final AtomicReferenceArray<Object[]> tree;
26+
private final Object[][] tree;
2927
private final IntFunction<T> mapper;
3028

3129
public RadixTreeCache(int level1, int level2, IntFunction<T> mapper, int... commonValues) {
32-
this.tree = new AtomicReferenceArray<>(level1);
30+
this.tree = new Object[level1][];
3331
this.mapper = mapper;
3432
this.level1 = level1;
3533
this.level2 = level2;
@@ -50,31 +48,15 @@ public T get(int primitive) {
5048
}
5149

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

0 commit comments

Comments
 (0)