|
24 | 24 | import org.openjdk.jcstress.annotations.JCStressTest; |
25 | 25 | import org.openjdk.jcstress.annotations.Outcome; |
26 | 26 | import org.openjdk.jcstress.annotations.State; |
| 27 | +import org.openjdk.jcstress.infra.results.II_Result; |
27 | 28 | import org.openjdk.jcstress.infra.results.L_Result; |
28 | 29 |
|
29 | 30 | import com.google.errorprone.annotations.Var; |
|
39 | 40 | * the correct visibility ordering. |
40 | 41 | * <p> |
41 | 42 | * {@snippet lang="shell" : |
42 | | - * // JAVA_VERSION=?? for an alternative jdk |
43 | | - * ./gradlew caffeine:jcstress --rerun |
| 43 | + * # use JAVA_VERSION for an alternative jdk |
| 44 | + * JAVA_VERSION=11 ./gradlew caffeine:jcstress --tests IntermittentNull --rerun |
44 | 45 | * } |
45 | 46 | * |
46 | 47 | * @author [email protected] (Ben Manes) |
47 | 48 | */ |
48 | | -@State |
49 | | -@JCStressTest |
50 | | -@Outcome(id = "ok", expect = Expect.ACCEPTABLE, desc = "Reference seen and valid") |
51 | | -@Outcome(id = "null", expect = Expect.FORBIDDEN, desc = "Reference seen but field not visible") |
52 | | -public class IntermittentNull { |
53 | | - private static final VarHandle VALUE; |
54 | | - |
55 | | - volatile Value value = new Value("ok"); |
56 | | - |
57 | | - @Actor |
58 | | - public void writer() { |
59 | | - var oldValue = (Value) VALUE.getAcquire(this); |
60 | | - var newValue = new Value("ok"); |
61 | | - VALUE.setRelease(this, newValue); |
62 | | - VarHandle.storeStoreFence(); |
63 | | - oldValue.data = null; |
64 | | - } |
| 49 | +@SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass") |
| 50 | +public final class IntermittentNull { |
| 51 | + |
| 52 | + private IntermittentNull() {} |
| 53 | + |
| 54 | + @State |
| 55 | + @JCStressTest |
| 56 | + @Outcome(id = "ok", expect = Expect.ACCEPTABLE, desc = "Reference seen and valid") |
| 57 | + @Outcome(id = "null", expect = Expect.FORBIDDEN, desc = "Reference seen but field not visible") |
| 58 | + public static class Simple { |
| 59 | + private static final VarHandle VALUE; |
| 60 | + |
| 61 | + volatile Value value = new Value("ok"); |
| 62 | + |
| 63 | + @Actor |
| 64 | + public void writer() { |
| 65 | + var oldValue = (Value) VALUE.getAcquire(this); |
| 66 | + var newValue = new Value("ok"); |
| 67 | + VALUE.setRelease(this, newValue); |
| 68 | + VarHandle.storeStoreFence(); |
| 69 | + oldValue.data = null; |
| 70 | + } |
| 71 | + |
| 72 | + @Actor |
| 73 | + public void reader(L_Result r) { |
| 74 | + @Var var value = (Value) VALUE.getAcquire(this); |
| 75 | + for (;;) { |
| 76 | + var data = value.data; |
| 77 | + if (data != null) { |
| 78 | + r.r1 = data; |
| 79 | + return; |
| 80 | + } |
| 81 | + VarHandle.loadLoadFence(); |
| 82 | + var current = (Value) VALUE.getAcquire(this); |
| 83 | + if (value == current) { |
| 84 | + r.r1 = null; |
| 85 | + return; |
| 86 | + } |
| 87 | + value = current; |
| 88 | + } |
| 89 | + } |
65 | 90 |
|
66 | | - @Actor |
67 | | - public void reader(L_Result r) { |
68 | | - @Var var value = (Value) VALUE.getAcquire(this); |
69 | | - for (;;) { |
70 | | - var data = value.data; |
71 | | - if (data != null) { |
72 | | - r.r1 = data; |
73 | | - return; |
| 91 | + static { |
| 92 | + try { |
| 93 | + VALUE = MethodHandles.lookup().findVarHandle(Simple.class, "value", Value.class); |
| 94 | + } catch (ReflectiveOperationException e) { |
| 95 | + throw new ExceptionInInitializerError(e); |
74 | 96 | } |
75 | | - VarHandle.loadLoadFence(); |
76 | | - var current = (Value) VALUE.getAcquire(this); |
77 | | - if (value == current) { |
78 | | - r.r1 = null; |
79 | | - return; |
| 97 | + } |
| 98 | + |
| 99 | + static final class Value { |
| 100 | + @Nullable String data; |
| 101 | + |
| 102 | + Value(String data) { |
| 103 | + this.data = data; |
80 | 104 | } |
81 | | - value = current; |
82 | 105 | } |
83 | 106 | } |
84 | 107 |
|
85 | | - static { |
86 | | - try { |
87 | | - VALUE = MethodHandles.lookup().findVarHandle(IntermittentNull.class, "value", Value.class); |
88 | | - } catch (ReflectiveOperationException e) { |
89 | | - throw new ExceptionInInitializerError(e); |
| 108 | + @State |
| 109 | + @JCStressTest |
| 110 | + @Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "Reference seen and valid") |
| 111 | + @Outcome(expect = Expect.FORBIDDEN, desc = "Reference seen but field not visible") |
| 112 | + public static class Actual { |
| 113 | + private static final String OK = "ok"; |
| 114 | + |
| 115 | + final Cache<String, String> cache; |
| 116 | + |
| 117 | + public Actual() { |
| 118 | + cache = Caffeine.newBuilder().weakValues().build(); |
| 119 | + cache.put(OK, OK); |
90 | 120 | } |
91 | | - } |
92 | 121 |
|
93 | | - static final class Value { |
94 | | - @Nullable String data; |
| 122 | + @Actor |
| 123 | + public void writer(II_Result r) { |
| 124 | + r.r1 = (cache.asMap().put(OK, OK) == null) ? 0 : 1; |
| 125 | + } |
95 | 126 |
|
96 | | - Value(String data) { |
97 | | - this.data = data; |
| 127 | + @Actor |
| 128 | + public void reader(II_Result r) { |
| 129 | + r.r2 = (cache.getIfPresent(OK) == null) ? 0 : 1; |
98 | 130 | } |
99 | 131 | } |
100 | 132 | } |
0 commit comments